持續測試與交付:品質來自快回饋,不是守關卡
· tech
📑 目錄
三個核心實踐的第二個。上一篇把一切變成程式碼之後,下一個問題自然是:程式碼會錯,怎麼知道這次變更是安全的?書的答案跟傳統直覺相反——不是在上線前設一道更嚴的關卡,而是把驗證拆碎、塞進工作的每一步。品質是快回饋餵出來的,不是關卡守出來的;這跟第一篇「大批次才是風險來源」是同一件事的執行版。
為什麼強調「持續」:堆著沒驗證的工作,就是堆風險
「持續」兩個字各有所指:持續測試是指邊做邊測——寫一小段就得到回饋,而不是整包寫完才送測;持續交付是指每個變更都保持在「隨時可上線」的狀態,而不是攢一批一起衝。反面教材大家都熟:基礎設施改動累積兩週,一次 apply 上去,炸了之後沒人知道是十七個變更裡的哪一個。變更批次越小、驗證越早,出錯的定位成本越低——這是全書從頭貫穿到尾的等式。
基礎設施的測試金字塔
軟體測試金字塔搬到基礎設施上一樣成立:越下層越快、越便宜、量越多;越上層越慢、越貴、越少——而且大部分的蠢事,在最下層就該被擋下來:
各層對應的實體大概是:最底層跑 terraform validate、lint、policy as code(「不准開 0.0.0.0/0 的 security group」這種規則寫成自動檢查);第二層做模組的單元測試、看 plan 的 diff;第三層真的把 stack 起在測試用的專案裡,驗證「建得起來、連得上」;頂層才是跨系統的整合驗證。
宣告式程式碼的測試陷阱:別測「code 說了什麼」
這章有個很誠實的提醒,省掉很多白工:宣告式程式碼的「單元測試」很容易寫成廢話——定義檔寫「開一台 t3.medium」,測試驗證「有開一台 t3.medium」,這只是把宣告再講一遍,永遠不會紅,紅了也只代表你忘了同步改測試。宣告式的低層驗證該花在會變的部分:吃了不同的變數、不同的組合後輸出對不對、模組的條件邏輯走對邊沒有;至於「宣告的東西真的建得起來、建起來真的能用」,交給上層拿真環境驗——測結果,不是測宣告。
一條 pipeline,一路晉級到正式環境
把上面的層次串起來的就是 delivery pipeline:
兩個設計原則值得畫重點:便宜的檢查放前面(大多數錯誤秒級陣亡,省下起環境的錢和等待);每個環境用同一份定義、同一條路徑部署——test 環境驗過的東西之所以可信,是因為 staging 和 production 拿到的是一模一樣的流程,而不是「概念上類似」的另一套。
反思
環境失真,是 infra 事故最常見的溫床
「staging 驗過了,上 prod 還是炸」——每次追下去,原因幾乎都是同一種:兩個環境根本不是同一份定義生出來的。staging 被人手動調過、版本落後三個月、某個資源是當年手刻的,於是它驗證的其實是另一個平行世界。這章給了我一個更好的說法:測試環境的價值不在「它長得像 prod」,在「它跟 prod 走同一條 pipeline、吃同一份 code」——像不像是結果,同不同源才是因。所以我現在把「staging 可以手動改」視為跟「prod 可以手動改」同罪:改的那一刻,它的驗證能力就歸零了。
先鋪最底層:policy as code 是性價比之王
想到測試很多人直接衝去做最上層的 e2e——貴、慢、flaky,然後放棄。金字塔給的順序剛好相反:先把秒級那層鋪滿。lint、validate、幾條 policy 規則,一個下午就能上線,從此「security group 開全世界」「資源忘了打 tag」「機器規格手滑選到 8xlarge」這類蠢事再也進不了 main——而回顧起來,真正燒錢的事故大多是這種等級的錯,不是什麼深奧的架構問題。用最便宜的一層擋掉最大宗的錯,剩下的預算再去買上層的信心,這個順序跟 SRE 講測試的結論殊途同歸。
長壽 branch 對 infra code 加倍致命
「持續」的反面是囤積。應用程式的 feature branch 放兩週,merge 時痛的是 code conflict;infra code 的 branch 放兩週,痛的是世界已經變了——你 branch 上的 plan 是對兩週前的現況算的,期間別人改過的網路、升過的版本、加過的資源,全都不在你的視野裡,merge 後第一次 apply 就是開獎。所以 trunk-based 對 infra 不是風格偏好,是止痛藥:變更小到當天能進 main、進了 main 就被 pipeline 帶著走完全程,「我的 branch 跟現實的差距」這個風險才會趨近於零。