「現在 AI coding agent 這麼強,重構 legacy 系統不就是叫它動手改就好了嗎?」
這大概是我開始這個系列前,最常被問到、也最常自問的一句話。答案是:AI 從來不是重構 legacy 系統的瓶頸,AI 會不會「亂改」才是。
我接手的是一套原生 PHP、無框架、已經運作多年的系統——沒有型別系統幫你擋錯誤、沒有現成的測試網把關、資料庫裡躺著上億筆歷史資料,還有好幾套跨版本並存、彼此有部分程式碼互相複製過的子系統。這種系統丟給 AI agent 動手,如果沒有配套的流程,最快的路徑往往就是最危險的路徑:AI 會很有自信地告訴你「已經確認過了,這是安全的」,但那個「已經確認過」的範圍,可能只涵蓋了整個系統的一小角。
這個系列要記錄的,不是「叫 AI 生成程式碼」這種表面示範,而是一整套讓 AI 安全動手的方法論:怎麼設計流程讓 AI 不亂改、怎麼建立驗證機制抓出 AI 引入的迴歸、怎麼把 AI 的長期記憶用在團隊協作上。
先講清楚一件事:接下來 30 天用的技術細節(PHP 7.4、pcov coverage、composer 版本管理)是 PHP 生態的實現方式,但這些技術背後的原則是語言無關的。無論你手上是 Java、TypeScript 還是 Python 寫的 legacy 系統,「AI 動手前要先有基準線」「乾淨環境比對排除雜訊」「AI 過早下結論的邊界在哪裡」這些判斷邏輯完全遷移得過去——換的只是工具,不是判斷邏輯本身。之後每篇技術工具比重比較重的地方,我會標明哪些是 PHP 特定的做法、對應到其他語言大概是什麼等效工具。
在重構過程中,有一次我請 AI 判斷「這批外部廠商是不是都已經沒有真實流量了,可以放到優先序後面」。AI 查了資料庫,很快回報:都沒有真實資料,判定為低優先序。
我追問了一句:「這幾家真的都沒有 key 可以打 API 看看嗎?」
AI 才意識到自己判斷的資料庫查詢範圍,其實只涵蓋了「一個站台」——而這套系統的站台設定表是全站共用的,同一家廠商的真實流量,完全可能分散在別的站台上。重查之後,原本判定「沒有資料」的名單裡,超過一半其實都有真實流量,只是分散在別處。
我沒有否定 AI 的查詢方法,那個 SQL 本身寫得沒問題。問題出在 AI 把「在我查過的範圍內找不到」直接等同於「不存在」——這正是我認為 AI 重構 legacy 系統時最危險的模式:不是給錯答案,而是給了一個看起來很完整、其實範圍不夠全面的答案,而且講得很有信心。
用一組對照來看這個差異:
❌ 沒有把關的版本:
AI:「已查證這 7 家廠商都沒有真實流量,可以列為低優先序。」
→ 查詢範圍只涵蓋了預設站台,結論看起來完整,實際上是錯的
✅ 有把關流程的版本:
AI:「已查詢預設站台,這 7 家沒有真實流量;
但注意站台設定是全站共用的,尚未排除其他站台的可能性,
建議查詢全站範圍後再下結論。」
→ 誠實標注查詢範圍的邊界,而不是把「沒查到」講成「確定沒有」
這兩個版本的差別,不是 AI 能力強弱的差別,而是有沒有一套逼 AI 誠實標注查詢邊界、且有人在關鍵節點追問一句「你查的範圍夠不夠」的流程。這正是接下來 29 天要講的東西。
AI 給出的「已確認」結論,永遠只在它實際查過的範圍內成立——重構 legacy 系統最危險的不是 AI 改錯程式碼,而是 AI 對自己查證範圍的信心,比它實際查證的範圍大。
這句話會貫穿整個系列。後面會看到同一種模式反覆出現在不同情境:DI 容器 autowire 出一個看起來正常、實際是空殼的物件;本機驗證全部綠燈、換到 CI 環境卻炸開;測試斷言被悄悄放寬到「能通過就好」而不是「驗證了正確的事」。這些表面上是不同的技術問題,骨子裡都是同一件事——AI 的自信範圍,需要一套流程去收斂到跟它實際查證的範圍一致。
如果你也在用 AI agent 處理你手上的 legacy 系統,回想一下:上一次 AI 跟你回報「已確認沒問題」的時候,你有沒有追問過一句「你查證的範圍是什麼」?那個答案,你自己驗證過了嗎?
明天會具體描繪這套 legacy 系統的樣貌——無框架、缺乏測試覆蓋、跨版本並存代表著什麼,以及這些特徵各自會怎麼放大 AI 亂改的風險。
延伸閱讀:本次鐵人賽同時並行的其他四個系列,會從不同角度處理相關的經驗,有興趣可以一起追: