iT邦幫忙

2026 iThome 鐵人賽

DAY 2
0
Claude AI

用 AI Agent 重構一套無框架的 legacy PHP 系統系列 第 2

Day 02:Legacy 系統的典型樣貌——無框架、無測試、跨版本並存的痛

  • 分享至 

  • xImage
  •  

前言:「無框架」聽起來很簡單,為什麼反而更難重構?

「沒有框架不是應該更自由嗎?想怎麼改就怎麼改,哪來的複雜?」

這是我常聽到的另一個誤解。框架的價值從來不是「限制你」,而是「幫你擋掉一整類錯誤」:路由怎麼派送是固定的、依賴怎麼注入是固定的、輸入怎麼驗證有慣例可循。拿掉框架之後,這些「本來不用想」的事全部變成要靠人(或 AI)自己判斷的細節——而 AI 對「這件事在這個專案裡是怎麼運作的」這種隱性知識,完全沒有安全網。

昨天提到的核心命題——「AI 給出的『已確認』結論,永遠只在它實際查過的範圍內成立」——在無框架、無測試、跨版本並存的系統裡,會被放大成三種不同形狀的風險。今天就把這三種風險具體攤開來看。

今日目標

  • 理解「無框架」對 AI 重構造成的具體風險,不是抽象的「比較亂」
  • 理解「無測試覆蓋」為什麼是重構能不能動手的先決條件,而不是事後補的加分項
  • 用一個真實案例,理解「跨版本並存」系統裡,AI 判斷 bug 歸屬會踩到什麼坑
  • 建立「重構前先問三個問題」的檢查習慣

風險一:無框架——沒有慣例可循,AI 只能用命名猜測

框架系統裡,「這個變數是什麼型別」通常有慣例可查:ORM 的 schema、型別宣告、IDE 的自動完成。無框架的 legacy 系統裡,這些線索常常只剩下命名

我接手的這套系統裡,資料庫欄位的命名慣例是用前綴表達型別——n 開頭代表數字、s 開頭代表字串。這個慣例大部分時候是對的,直到某一次要把某張表的欄位轉成 Repository 層的型別轉換宣告時,AI 直接照命名慣例判斷:nTotalAmt 開頭是 n,應該轉成整數型別。

實際去查建表的 migration 才發現,這個欄位其實是 decimal——存的是帶小數的金額。命名慣例騙人了,而且騙得很有說服力,因為系統裡九成以上的 n 開頭欄位真的是整數。

❌ 沒有查證的版本:
AI:「欄位名稱 nTotalAmt 以 n 開頭,依命名慣例轉型為 int。」
→ 命中大多數情況,但這裡剛好是例外,會導致小數金額被截斷

✅ 查證過真實 schema 的版本:
AI:「已查詢 migration 檔案,nTotalAmt 實際型別為 decimal(18,4),
     依實際型別轉型,不依賴命名慣例推斷。」
→ 命名慣例可以當提示,但不能取代查證實際定義

沒有框架強制的型別系統,命名慣例就是唯一的線索,而線索是會騙人的——AI 重構無框架系統時,任何「依慣例判斷」的地方,都要換成「依實際定義查證」。

風險二:無測試覆蓋——重構的先決條件,不是加分項

第二個風險更根本:這套系統裡,很多程式碼從寫成的那一天起,就沒有任何自動化測試覆蓋過。

一開始我以為「先重構、之後有空再補測試」是合理的節奏。後來意識到這個順序在 AI 重構的情境下特別危險:沒有測試覆蓋的程式碼,AI 改完之後,你唯一能確認「有沒有壞掉」的方法,是靠人工重新走一遍所有情境——而這正是 AI 重構規模化之後,最先失守的一環,因為人工驗證的速度追不上 AI 改動的速度。

我現在的規則很直接:目標程式碼現在有沒有測試覆蓋,是重構能不能動手的先決條件,不是「順便補一下」的加分項。沒有覆蓋就先停手,除非這次重構的目標本身就是「幫這段程式碼補上測試」。這聽起來像是把節奏拖慢,但實際上是把「事後才發現壞掉」的風險,換成「事前就知道哪裡不能碰」的確定性。

風險三:跨版本並存——同一段程式碼,一套是對的,另一套是壞的

第三個風險最隱蔽,也最容易讓 AI 判斷錯誤。這套系統的正式環境上,同時存在好幾套彼此獨立、卻又有部分程式碼互相複製過的平行系統——因為早期開發時圖方便,直接把一套系統的程式碼複製到另一套系統當起點,複製之後兩邊各自演化,但沒有人回頭把共通的部分抽出來統一維護。

某次請 AI 判斷一段可疑程式碼是不是 bug,AI 很快查出這段邏輯確實有問題——某個資料結構的欄位存取方式跟目前的定義對不上。但這段程式碼其實屬於「另一套平行系統」複製過來、沒有跟著改的殘留片段,在它原本所屬的那套系統裡,資料結構是另一種形狀,這段程式碼在那邊是對的;只是被複製到現在這套系統之後,兩邊的資料結構已經分岔,這裡才是真正壞掉的地方。

判斷「這是不是 bug」之前,得先確認這段程式碼真正屬於哪一套系統——歸因錯了,後面的修法方向就整個錯了。 這不是程式碼邏輯本身的問題,是背景知識的問題,而背景知識恰恰是 AI 在一次性任務裡最容易漏掉的東西。

重構前先問三個問題

把這三種風險收斂成一個動手前的檢查習慣:

  1. 這段判斷是依賴命名慣例,還是依賴實際定義?——無框架系統裡,命名只是提示,不是保證
  2. 目標程式碼現在有沒有測試覆蓋?——沒有就先停手,除非這次重構本身就是補測試
  3. 這段程式碼真正屬於哪一套系統?——跨版本並存的系統裡,先確認歸屬,再判斷對錯

這三個問題背後其實是同一件事:逼 AI 把「看起來合理的推斷」換成「查證過的事實」,跟昨天講的核心命題完全一致——AI 的自信範圍,需要一套流程收斂到跟它實際查證的範圍一致。

今日思考題

回想你手上的 legacy 系統:有沒有哪個地方是靠「命名慣例」「大家都這樣寫」在維持運作,而不是靠明確的規則?如果 AI 今天要改動那個地方,它有辦法查到真正的定義,還是只能猜?

今日重點回顧

  • 無框架系統裡,命名慣例可能騙人,AI 判斷型別/結構前要查證實際定義,不能只依賴命名
  • 無測試覆蓋的程式碼,重構前要先停手——這是先決條件,不是事後補的加分項
  • 跨版本並存的系統裡,判斷 bug 前要先確認程式碼真正屬於哪一套系統,歸因錯了修法方向就錯了
  • 重構前先問三個問題:依慣例還是依定義?有沒有測試覆蓋?屬於哪一套系統?

明日預告

明天會講一個更直接的案例——一次沒有安全網的重構,AI 引入了一個真正的迴歸,而且差一點就被合併進主線。這個案例會具體示範,今天講的「無測試覆蓋」風險,實際發生時長什麼樣子。


上一篇
Day 01:AI 重構 legacy 系統的真正難點在哪裡
系列文
用 AI Agent 重構一套無框架的 legacy PHP 系統2
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言