🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

就因為package.json裡少了個^號,我們公司賠了客戶十萬塊

image.png

寫這篇文章的時候,我剛通宵處理完一個P0級(最高級別)的線上事故,天剛亮,煙灰缸是滿的🚬。

事故的原因,說出來你可能不信,不是什麼伺服器宕機,也不是什麼駭客攻擊,就因為我們package.json裡的一個依賴項,少寫了一個小小的^(脫字符號)

這個小小的失誤,導致我們給客戶A的數據計算模組,在一次平平無奇的依賴更新後,全線崩潰。而我們,直到客戶的業務方打電話來投訴,才發現問題。

等我們回滾、修復、安撫客戶,已經是7個小時後。按照合同的SLA(服務等級協議),我們公司需要為這次長時間的服務中斷,賠付客戶十萬塊

老闆在事故復盤會上,倒沒說什麼重話,只是默默地把合同複印件放在了桌上。

滿臉寫著無奈.gif

今天,我不想抱怨什麼,只想把這個價值 十萬塊 的教訓,原原本本地分享出來,希望能給所有前端、乃至所有工程師,敲響一個警鐘。


事故是怎麼發生的?

我們先來復盤一下事故的現場。

我們有一個給客戶A訂製的Node.js數據處理服務。它依賴了我們內部的另一個核心工具庫@internal/core

在項目的package.json裡,依賴是這麼寫的:

{
  "name": "customer-a-service",
  "dependencies": {
    "@internal/core": "1.3.5",
    "express": "^4.18.2",
    "lodash": "^4.17.21"
    // ...
  }
}

注意看,expresslodash前面,都有一個^符號,而我們的@internal/core沒有

這個^代表什麼?它告訴npm/pnpm/yarn:“我希望安裝1.x.x版本裡,大於等於1.3.5最新版本。”

而沒有^,代表什麼?它代表:我安裝1.3.5這一個版本,鎖死它,不許變。

問題就出在這裡。

上週,core庫的同事,修復了一個嚴重的性能Bug,發布了1.3.6版本,並且在公司群裡通知了所有人。

我們組裡負責這個專案的同學,看到了通知,也很負責任。他想:core庫升級了,我也得跟著升。

於是,他看了看package.json,發現專案裡用的是1.3.5。他以為,只要他去core庫的倉庫,把1.3.5這個tag刪掉,然後把1.3.6的tag打上去,CI/CD在下次部署時,重新pnpm install,就會自動拉取到最新的代碼。

他錯了!


最致命的鎖死版本

因為我們的依賴寫的是"1.3.5",而不是"^1.3.5",所以我們的pnpm-lock.yaml文件裡,把這個依賴的解析規則,徹底鎖死在了1.3.5

無論core庫的同事怎麼發布1.3.61.3.7,甚至2.0.0...

只要我們不去手動修改package.json,我們的CI/CD流水線,在執行pnpm install時,永遠、永遠,都只會去尋找那個被寫死的1.3.5版本。

然後,災難發生了。

core庫的同事,在發布1.3.6後,為了保持倉庫整潔,就把1.3.5那個舊的git tag刪掉了

然後,客戶A的專案,某天下午需要做一個常規的文案更新,觸發了部署流水線。

流水線執行到pnpm install時,pnpm拿著lock文件,忠實地去找@internal/[email protected]這個包...

“Error: Package '1.3.5' not found.”

流水線崩潰了。一個本該5分鐘完成的文案更新,導致了整個服務7個小時的宕機😖


十萬塊換來的血淚教訓

事故復盤會上,我們所有人都沉默了。我們復盤的,不是誰的鍋,而是我們對依賴管理這個最基礎的認知,出了多大的偏差。

^ (Caret) 和 ~ (Tilde) 不是選填,而是必填

  • ^ (脫字符)^1.3.5 意味著 1.x.x (x >= 5)。這是最推薦的寫法。它允許我們自動享受到所有 非破壞性 的小版本和補丁更新(比如1.3.61.4.0),這也是npm install默認的行為。
  • ~ (波浪號)~1.3.5 意味著 1.3.x (x >= 5)。它只允許補丁更新,不允許小版本更新。太保守了,一般不推薦。
  • (啥也不寫)1.3.5 意味著鎖死。除非你是reactvue這種需要和生態強綁定的宿主,否則,永遠不要在你的業務專案裡這麼幹!

我們團隊現在強制規定,所有package.json裡的依賴,必須、必須、必須使用^

關於lock文件

我們以前對lock文件(pnpm-lock.yaml, package-lock.json)的理解太淺了,以為它只是個緩存。

現在我才明白,package.json裡的^1.3.5,只是在定義一個規則。

pnpm-lock.yaml,才是基於這個規則,去計算出的最終答案。

lock文件,才是保證你同事、你電腦、CI伺服器,能安裝一模一樣的依賴樹的唯一路徑。它必須被提交到Git

依賴更新,是一個主動的行為,不是被動的

我們以前太天真了,以為只要依賴發了新版,我們就該自動用上。

這次事故,讓我們明白:依賴更新,是一個嚴肅的、需要主動管理和測試的行為。

我們現在的流程是:

image.png

  1. 使用pnpm update --interactivepnpm會列出所有可以安全更新的包(基於^規則)。
  2. 本地測試:在本地跑一遍完整的測試用例,確保沒問題。
  3. 提交PR:把更新後的pnpm-lock.yaml文件,作為一個單獨的PR提交,並寫清楚更新了哪些核心依賴。
  4. CI/CD驗證:讓CI/CD在staging環境,用這個新的lock文件,跑一遍完整的E2E(端到端)測試。

這十萬塊,是技術Leader(我)的失職,也是我們整個團隊,為基礎不牢付出的最昂貴的一筆學費。

一個小小的^,背後是整個npm生態的依賴管理的核心。

分享出來,不是為了博眼球,是真的希望大家能回去檢查一下自己的package.json

看看你的依賴前面,那个小小的^,它還在嗎?😠


原文出處:https://juejin.cn/post/7568418604812632073


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝22   💬4   ❤️6
681
🥈
我愛JS
📝1   💬4   ❤️2
45
🥉
酷豪
1
#5
1
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付