「跑測試,紅字,代表這次改動有問題」——這句話聽起來理所當然,但在一套運作多年、缺乏完整測試網的系統上,這個推論其實有一個沒說出口的前提:你要先確定,這些紅字在你改動之前就不存在。
前五天講的是動手前的準備:確認分支基準、用覆蓋率門檻決定哪些程式碼真的敢改、驗證 PHP 版本相容性。這些都是「動手前」要做的事。從今天開始,我們正式進入這個系列的第二部——讓 AI 安全動手的核心機制。而第一個要處理的問題,發生在「動手之後」:改完之後測試紅了,這代表迴歸嗎?
答案沒有那麼直覺。我接手的這套系統歷史夠長、環境夠複雜,本機驗證環境本身就會產生雜訊——殘留的測試資料、PHP 版本落差造成的套件行為差異、甚至單純只是某個測試本來就不穩定(flaky)。如果 AI 看到紅字就直接判定「是我這次改動造成的」,然後開始「修」,很可能是在修一個根本不存在的問題,還可能把本來沒事的地方越改越糟。
假設你請 AI 對一段 legacy 程式碼做了收斂——把幾十處手寫 SQL 改成透過 Repository 呼叫。改完之後跑一次全套測試,出現了 12 個失敗。
這 12 個失敗代表什麼?可能性至少有三種:
只跑一次測試,永遠回答不了「這 12 個各自屬於哪一種」。你需要一個對照組。
實務上的做法很直接:
git stash 把改動暫存起來,讓工作目錄回到改動前的乾淨狀態git stash pop 把改動還原回來差集是空的,代表這次改動沒有引入任何新迴歸,那 12 個失敗全部是環境本來就有的雜訊,可以放心繼續往下走。差集不是空的,才代表真的有東西要處理——而且處理範圍精準縮小到差集裡列出的那幾個測試,不用大海撈針。
用一組對照看差異:
❌ 沒有比對基準的版本:
AI:「跑完測試,12 個失敗,這次改動看起來有迴歸,我來修一下。」
→ 直接假設全部 12 個都是這次改動造成的
→ 可能把跟改動無關的舊有失敗也「修」了一遍,
引入新的、跟原始需求無關的變更
✅ 有乾淨基準比對的版本:
AI:「帶改動跑測試:12 個失敗。
git stash 後跑乾淨基準:11 個失敗(同一批舊有失敗一直都在)。
差集:1 個新增失敗,是 XxxTest::yyy_test。
這是這次改動唯一造成的迴歸,其餘 11 個跟這次改動無關。」
→ 精準定位真正需要處理的範圍,不擴大戰場
這兩個版本的差別不是 AI 細不細心,是有沒有一個對照組。沒有乾淨基準,AI 對「測試紅了」這件事的判斷,跟丟硬幣猜差不多。
這道比對聽起來簡單,但實際跑起來有兩個實務上的理由,不適合放在主線程直接做:
第一,全套測試通常要跑好幾分鐘,帶改動跑一次、乾淨基準再跑一次,等於要等兩倍時間。如果這個過程佔用主對話的上下文,會讓後續每一輪互動都要背著這兩次測試的完整輸出(動輒上千行的測試框架輸出),拖慢整個對話,也讓真正該關注的訊息被稀釋掉。
第二,也是更重要的一點:這個過程本身容易產生誤判的誘惑。如果 AI 一邊跑測試一邊在同一個上下文裡「順手」判斷「這幾個失敗應該是無關的吧」,很容易變成沒有真的做完整比對、單憑印象就下結論。把這個流程獨立出去,逼自己完整跑完兩次、產生一份確實的差集報告,再回到主線程根據這份報告討論怎麼處理,能有效避免「跑到一半就開始腦補結論」的捷徑心態。
回報格式也要固定下來:差集是空的,要明講「沒有新增迴歸」,不能只說「看起來沒問題」;差集不是空的,要列出完整測試名稱,不能用「有幾個失敗跟這次改動有關」這種摘要帶過。
這道比對機制裡,還藏著一條更重要的邊界:AI 只負責跑出乾淨的差集報告,不負責判斷「這個差集裡的失敗能不能忽略」。
這個界線的理由跟系列第一天講的那句話是同一件事——AI 給出的「已確認」結論,永遠只在它實際查過的範圍內成立。差集比對能確認「這個失敗是不是這次改動造成的」,但確認不了「這個失敗重不重要」「能不能先擱置」。這種帶著業務判斷、風險承受度考量的決定,不該讓 AI 自己拍板,得交回給人。
我曾經看過一個相反的例子:某次改動的差集裡出現了一個新增失敗,AI 判斷「這個測試斷言的是一個邊界情境,實務上流量很小,應該可以先不管」,就這樣把它晾在一邊繼續往下做。這個判斷聽起來合理,但「流量小」是對當下資料現況的觀察,不代表這個邊界情境在協定設計上就是不重要的——這正是「用觀察到的資料現況,取代協定本身正不正確」的思維捷徑,跟這個系列後面會講到的另一個案例(判斷分頁邏輯要不要做完整,卻用「目前流量沒觸發」當理由不修)是同一種錯誤的兩個面貌。差集報告只負責把問題找出來、精準定位,要不要處理、什麼時候處理,是人的決定。
回想你自己重構時的經驗:測試紅字出現的當下,你是先假設「是我剛剛改壞的」,還是先去確認「這些失敗改動前就存在嗎」?如果從來沒做過乾淨基準比對,下次不妨試著跑一次,看看真正屬於你這次改動的紅字有多少——很可能比你以為的少。
git stash 後跑一次乾淨基準、取差集,只有差集裡的才是真迴歸明天要往下追問一層:為什麼這道比對、以及後面會提到的其他驗證流程,都建議放進獨立的 subagent 或子流程裡跑,而不是讓主線程一邊做一邊判斷?背後的理由不只是效能,還牽涉到「怎麼避免驗證過程本身污染判斷」這件事。