團隊一直說我們有品質關卡。紙面上聽起來令人安心:測試必須通過,評估必須達到預期,發佈必須在往前推進之前滿足某些檢查。但當我回顧一整季的紀錄時,一個更令人不安的問題浮現了:這些關卡之中,究竟有沒有任何一個真的擁有攔下任何東西的權力? 有好幾次,建置轉紅了,還是有人照樣合併。評估分數下滑了,卻沒有人跟進。成本暴增只在帳單寄達後才被發現。問題未必在於我們缺乏測試。我們有測試。我們缺乏的是一個明確定義的位置,讓某個結果真的能說出:「就停在這裡。」
這改變了我對品質關卡的看法。關卡並不只是碰巧在開發流程某處執行的測試。它是一條連結到交付流程、並有證據背書的決策規則。對每一道關卡,我想回答四個問題:進入之前什麼必須為真、什麼結果會阻擋進度、允許哪些例外、以及證據在哪裡? 這正是 CI/CD 與版本控制變得重要的地方。我們的團隊使用 CircleCI 來自動化建置、測試與部署流程,並將 pipeline 連結到團隊受版本控制的儲存庫。CircleCI 提供了自動化層,可以執行這些檢查,並控制工作如何沿著交付流程推進。
檢驗一道品質關卡是否為真,最簡單的方法是問:「它失敗時會發生什麼?」 如果答案是「有人會去看」、「我們會討論」或「我們通常會修掉」,那我仍然無從得知它是不是一道關卡。阻擋條件必須帶來實際的後果。如果必要的 CI 作業失敗了,該變更就不應被視為可以合併。如果必要的評估低於約定門檻,發佈就應該停止。如果 prompt 在沒有必備迴歸證據的情況下被更改,該變更就不應僅僅因為應用程式看起來還能運作,就被視為已驗證。
這正是版本控制與 CI/CD 相輔相成之處。版本控制系統告訴我我看的是哪一個變更;CI/CD 則告訴我那個變更被測試時發生了什麼。CircleCI 能因應受版本控制儲存庫中的變更而執行 pipeline,並讓 pipeline 與作業結果成為開發流程中可見的一部分。對 QA 來說,這形成了一條有用的鏈:受版本控制的變更 → CI/CD pipeline → 測試結果 → 關卡決策。一次失敗後仍清楚顯示為失敗的建置,就是證據。一次被人手動忽略、卻沒有留下任何例外紀錄的失敗建置,就不是一個運作中的關卡。
第二個問題是:「我憑什麼相信它通過了?」 一份有用的通過紀錄,應該指出測試了什麼、在哪裡測試、涉及哪個版本或 commit、何時執行,以及哪些案例支撐這個結果。CircleCI 提供 pipeline、workflow 與作業紀錄,有助於保存這條追溯線。對 QA 來說,這意味著結果不必依賴某人回憶自己昨天測過什麼。沒有痕跡的通過,和從來沒跑過之間,近得危險。
我不希望整套測試在每個階段原封不動地重跑,只因為重複讓人覺得安心。在每道關卡執行同一批檢查,可能增加可觀的延遲,卻帶來不了多少新證據。相反地,每道關卡應該有不同的職責。PR 關卡在變更進入審查之前保護它。合併關卡保護主線,不讓尚未準備好成為共享建置一部分的變更進入。發佈前關卡詢問候選版本是否已準備好出貨。正式環境關卡則在流量與真實營運條件導入時,保護線上系統。
一套 CI/CD 系統能讓這種區分不只是書面政策。在 CircleCI 中,workflow 可以定義哪些作業執行、它們的順序、相依關係與條件,必要時還包括需要人工批准的步驟。這意味著 workflow 本身就能體現關卡的一部分:後面的部署作業,在它所需的先前作業成功之前,不會獲得執行資格。舉例來說,一個簡化後的流程可以是:

每個階段只有在其所需的前一個階段成功之後才能執行:這個相依關係是由 pipeline 強制執行的,而不只是被記錄下來。虛線邊框標示唯一一個選用的步驟。
重點在於,pipeline 應該強制執行相依關係,而不只是把它寫下來。如果發佈前的評估失敗了,而正式環境部署依然暢行無阻,那麼這個評估就只是一項建議,不是關卡。
例外必須在有人需要它之前就設計好。如果一個失敗的檢查可以被繞過,流程就應該規定:誰有權授予例外、必須記錄什麼理由、這個決策記錄在哪裡,以及缺失的驗證必須何時補完。這在 CI/CD 中尤其重要,因為自動化可能製造虛假的安全感:擁有一條綠色的 pipeline,不代表每一條品質規則都存在;擁有一條 pipeline,也不自動代表每一次失敗都會攔下部署。Workflow 必須明確編碼哪些作業是必備的,以及哪些條件允許推進。
AI 功能會引入一般建置與部署檢查可能抓不到的失敗模式。應用程式可以編譯成功、API 可以回傳 200、前端可以正確渲染,而助理的行為卻仍然退化了。這就是為什麼我會加上至少三個 AI 專屬的阻擋條件:評估集的迴歸超出約定容許範圍、每次對話的成本或延遲超過上限,以及更改 prompt 卻沒有配套的迴歸執行。
前兩項問的是:「它變得更糟了嗎?」 一次 AI 變更可能在重要案例上降低品質,即便傳統的軟體測試仍然是綠的。同樣地,prompt 或模型的更動可能讓回應明顯變長、變慢或變貴,卻不產生任何顯而易見的功能故障。這些檢查應該掛在相關的 CI/CD workflow 上,而不是停留在非正式的觀察。如果 AI 評估作業回報品質掉出約定容許範圍,發佈 workflow 就應該把那個結果視為阻擋條件,而不只是多顯示一個沒有人被要求採取行動的紅綠數字。
第三個條件問的是另一個問題:「這個變更到底有沒有被記錄?」 如果 prompt 是系統行為的一部分,更改它卻不記錄版本、也不重跑相關評估,會讓 QA 無法解釋輸出為什麼變了。這正是版本控制特別重要的地方。理想情況下,prompt 產物、其版本中繼資料、其變更理由,以及其迴歸證據,應該與該次發佈之間存在可追溯的關係。如果 prompt 在這條追溯線之外被更改——例如直接改在正式環境的設定裡——應用程式的版本號可能維持不變,而它的行為卻在底下悄悄改變了。CI/CD pipeline 能保護透過它的流程進來的變更,但它無法自動證明某次未納入追蹤的正式環境修改從未發生。
*表格中的門檻與時限均為範例(演練資料),需要再調校。
| 關卡 | 進入條件 | 阻擋條件 | 例外途徑 | 證據 |
|---|---|---|---|---|
| PR | 分支可建置;變更附帶最小案例 | 靜態檢查失敗;單元測試轉紅;變更沒有對應案例 | PR 作者加上一位審查者同意,理由記錄在 PR 留言中,並在 24 小時內補上案例 | CI 執行日誌與失敗摘要 |
| 合併 | PR 關卡綠燈;合約測試通過 | 合約被破壞;整合測試轉紅;更改 prompt 卻沒有迴歸卡 | 測試負責人同意並開立追蹤票 | 合約測試報告;迴歸卡編號 |
| 發佈前 | 評估集落在可接受範圍內 | 迴歸超出容許範圍;成本或延遲超過上限;已知會失敗的對抗案例 | 產品負責人與測試負責人共同同意,附上風險註記與回滾計畫 | 評估對照表;成本與延遲表 |
| 正式環境 | 發佈前關卡綠燈;回滾已就緒 | Canary 指標異常;抽樣的壞案例率超過門檻 | 值班主管同意,流量設上限並附加額外觀察 | Canary 觀察日誌;抽樣清單 |
這張表可以讀成一連串後果逐級加重的決策:PR → 合併 → 發佈前 → 正式環境。與其把「測試必須通過」當成一條模糊的要求,每個階段都指明:它保護的是什麼、什麼條件會阻擋進度、必須存在什麼證據,以及當例外被授予時會發生什麼。在 CI/CD 的實作中,這些階段可以對應到作業與 workflow,由相依關係決定下一個階段是否獲准執行。CircleCI 的 workflow 提供了組織這些作業、控制其順序的結構,而 pipeline 則是把這些作業連結到版本控制與交付事件上、更大的工作單位。
這也讓版本控制與 QA 證據之間的關係更加清楚。一個變更始於一次受版本控制的 commit 或 pull request;CircleCI 檢出那段程式碼並執行設定的作業;產生的 pipeline 提供了發生過什麼的證據;而只有必備條件全部成功,workflow 才得以推進。部署紀錄也能幫忙把某次特定的發佈連結到 CI/CD 流程。目標不是把 CircleCI 變成品質的定義——而是運用自動化層去強制執行團隊早已定義好的那些品質決策。
這份規格涵蓋的是何時該攔,而不是由誰負責修復、或修復必須多快落地,它也不保證關卡的內容是正確的。如果評估集有偏,關卡可以忠實地放行錯誤的東西;一個自動化得再完美的 CircleCI workflow,仍然可能強制執行一套很糟的測試。成本與效能限制也一樣:一個精確的門檻,仍然可能是一個糟糕的產品決策。還有一條重要的界線,存在於受版本控制的變更與發生在 pipeline 之外的設定變更之間。CI/CD 能讓預定的路徑高度可見、高度可執行,但它無法變魔術般地讓一次沒有留下紀錄的正式環境修改變得可追溯。小團隊也可以把 PR 與合併兩道關卡合而為一,只要合併後的關卡仍然回答得了:它攔什麼、什麼證據能證明結果,以及條件失敗時會發生什麼。目標不是增加更多儀式或更多 CI 作業,而是讓**「關卡」**這個詞重新有意義:一條連結到某個版本的規則、有證據背書,而且真的有能力停下下一個階段。