首頁/人工智慧

改寫舊系統,難的是證明沒改壞:Claude Code 的 code-modernization 外掛

2026年10月19日 人工智慧 Claude Code AI Coding 舊系統改寫 測試 外掛

叫 AI 把一段舊程式改寫成新語言,現在已經不太難了。難的是改完之後,你怎麼知道它跟原本的行為一樣。

最常見的流程是:AI 改寫,AI 順手寫測試,測試全綠,合併。問題在於那些測試是同一個 AI 寫的,它只會測它想得到的情況。舊系統裡那些「月底遇到空白欄位會停下來」「負零要當成零」之類的怪行為,沒人寫進規格,也不會出現在測試裡,要等到上線後才會冒出來。

Anthropic 在官方外掛市集 claude-plugins-official 放了一個 code-modernization 外掛(目前版本 1.0.0),整個設計就是在處理這件事。我把它的 README、指令檔和代理設定讀過一遍,這篇整理它在做什麼、哪裡設計得好,還有還沒開始跑就該先知道的幾個地方。

左邊是 AI 自己寫測試、測試全綠、上線才發現差異;右邊是舊程式與新程式跑同一組輸入逐 byte 比對,再補新輸入和 canary,最後每個模組得到 PROVEN、PARTLY PROVEN 或 NOT PROVEN

它把「改寫」拆成九個步驟

安裝只要一行,在 Claude Code 裡輸入:

/plugin install code-modernization@claude-plugins-official

裝好之後有 13 個指令、8 個專門的代理(agent),另外有 6 支 workflow 腳本負責需要同時開很多代理的步驟。指令全名都長這樣:/code-modernization:modernize-assess,下面為了好讀只寫最後的動詞。

它的入口是 /code-modernization:modernize。這個指令會用彈出視窗問你兩件事:想對這份程式碼做什麼(升版、換技術重寫、重新設計架構,或者「先搞懂它就好」),以及有什麼必須維持不變(舊系統要繼續跑、行為要完全一致連怪癖都保留、順便修已知錯誤、先做資安檢查)。答案會寫進 analysis/<name>/INTENT.md,之後每個指令都讀這份檔案,不會再問一次。

兩列各五個步驟:modernize、preflight、assess、map、extract-rules,接著 review、brief、建置、verify、harden,下方標出六個需要人決定的地方

每一步都會產出一個檔案,也會更新 analysis/<name>/REPORT.html 這一頁總覽報告,所以你可以在任何一步停下來看。下一步要跑什麼,每個指令結尾都會直接給你可以貼上的那一行;忘了走到哪,就跑 modernize-status <name>。

我覺得最有意思的設計是那幾個橘色標籤。外掛明確列出六個地方一定要人來決定:回答 preflight 的五個問題、改正看起來不對的業務規則、核准計畫、接受驗證時發現的差異、簽核驗證結果、套用資安修補。這六件事它不會替你做,計畫沒核准前也不會開始寫任何新程式碼。

preflight 的五個問題也很實際:這是整個系統還是一部分、在這台機器上能不能建置和跑測試、有沒有自訂的建置工具、以前有沒有人試過改寫、有沒有什麼是不能碰的。這些問題 AI 讀程式碼答不出來,只能問人,所以 README 建議第一輪要找一個熟悉這套系統怎麼建置、怎麼部署的工程師在旁邊。

三種改法,計畫會建議其中一種

步驟 6 的建置分三種,看你要的是什麼:

你想要 指令 它怎麼做
同一套技術升到新版(.NET Framework 到 .NET 8、Java 8 到 17、Spring Boot 2 到 3) uplift 保留原本的程式碼,只修新版本會壞掉的地方。先做一個試點單元,把經驗寫成 playbook,再分批套用到其他單元。
換成另一種技術,一個模組一個模組換,舊系統繼續跑 transform 先寫測試把舊行為固定下來,再用新技術慣用的寫法重寫,最後新舊跑同一組輸入比對。
在新架構上重建 reimagine 從程式碼整理出規格,架構經過審查核准,再產生服務骨架和可執行的驗收測試。

uplift 因為保留原本的程式碼,可以跳過業務規則那兩步,preflight → assess → uplift → verify 就是一條完整的路。如果分析下來發現所謂的「升版」其實要改掉大部分程式碼(像 Python 2 到 3 到處都是 str 和 bytes 混用),它會直接說這應該改用 transform。

純搬家(rehost)和直接買現成產品(replace)不改程式碼,沒有對應的建置指令,但前面的分析一樣用得上。

業務規則寫成卡片,附上檔案和行號

步驟 4 的 extract-rules 是我覺得最值得單獨拿出來用的一步。它會把藏在舊程式碼裡的計算、檢核、資格判斷、狀態轉換、保留期限這類規則挖出來,每條寫成一張 Given/When/Then 卡片。代理設定檔裡給的範例長這樣:

Given an account with balance $1,250.00 and APR 18.5%
When the monthly interest batch runs
Then the interest charged is $19.27 (balance × APR ÷ 12, rounded half-up to cents)

用的是具體數字,不是「當餘額大於零時計算利息」這種抽象描述。1,250 × 18.5% ÷ 12 = 19.2708,四捨五入到分就是 19.27,你可以自己驗算。

每張卡片還要附上:

  • 規則在原始碼的 file:line 位置

  • 寫死在程式裡的參數(費率、上限、魔術數字),這些通常之後要改成設定值

  • 信心程度(High、Medium、Low),不是 High 的要寫出要問業務專家的那個問題

  • 優先等級,P0 是「錯了會讓系統失去意義,或代價很高、無法挽回」的規則

挖規則的代理跟檢查規則的代理是分開的,第二個代理會回頭對照每一條引用的程式碼。它也分得很清楚什麼不算業務規則:日誌、錯誤處理、技術性重試、連線池這些「換了語言就不存在」的東西都不收。

接著 review 會把有疑問的規則一條一條丟給人判斷:對、不對、不確定。結果寫進 RULE_REVIEWS.json,後面的計畫、建置和測試都會讀。它不會去改 BUSINESS_RULES.md 本身,人的判斷另外記一份。

就算你不打算讓 AI 改寫任何東西,光是這份附行號的規則清單,拿來做系統交接或補文件就已經很有用。入口那邊選「先搞懂它就好」,就是只跑到這裡加上計畫,不會產生任何新程式碼。

驗證是重做一次,不是看筆記

整個外掛最核心的是步驟 7 的 verify。README 建議在建置完之後開一個新的 session 跑它,因為它刻意不相信建置步驟留下的紀錄,而是自己重做一遍。

六張檢查卡片:從乾淨狀態重跑、新舊跑同一組輸入、沒人用過的輸入、測試要能失敗、P0 規則都有測試、原始碼沒被動過,匯入 proof_pack.py 算出三種判定

這六項檢查裡,我覺得有三個特別到位:

測試數字從測試工具自己的結果檔讀,不是從 AI 的回報抄。一次沒有執行任何測試的跑法直接算失敗。這擋掉了 AI 說「全部通過」但其實什麼都沒跑的狀況。

它會自己生出至少 10 組新輸入再比一次:邊界值、空值、超大資料、順序不對的資料,如果你要求連怪癖都要一致,還會加格式錯誤的紀錄。用意是抓出「測試只在 AI 自己挑的案例上會過」的情形。

canary 是故意在新程式碼裡改壞一行,測試必須變紅。連比對腳本本身也會被測:改掉輸出的一個 byte,看它有沒有抓到。測試能過不稀奇,測試要能失敗才有意義。

最後由 scripts/proof_pack.py 從這些結果檔算出每個模組一個判定:PROVEN、PARTLY PROVEN(會寫出缺什麼)、NOT PROVEN(沒跑、有差異、測試沒過)。每次都從現有的證據重新算,不沿用上一次的結果。

如果舊系統沒辦法在你手邊執行,例如大型主機上的 COBOL,或只能在正式環境跑的東西,就只能拿錄下來的輸出比對,這時最好的結果就是 PARTLY PROVEN,報告會直接寫出來,不會硬給你一個 PROVEN。

官方拿真實專案跑過的結果

README 有一節列出開發時拿公開專案實際跑過的結果,數字都是那些執行留下的檔案。我挑幾個比較有參考價值的:

專案 做什麼 結果
AWS CardDemo(COBOL) 每月利息批次改寫成 Java 21 35 條規則(8 條 P0),最後 213 個測試。連續五輪獨立驗證跟真的 COBOL 程式比對,每一輪都抓到測試漏掉的東西:一個會讓批次停下來的空白欄位、非數字的帳戶鍵、負零。最後一輪又試了 15 組含格式錯誤的輸入,找到 7 個差異,判定 NOT PROVEN。
Eclipse Jetty(2,600 個檔案) Java 8 升 17 試點 jetty-util 模組,946 個測試在兩個版本結果一致。實際建置執行後發現 6 個光讀程式碼沒預測到的變化,例如某個打包外掛產生無效的 manifest 卻仍然回報 SUCCESS。
osCommerce(PHP,約 44,000 行) 改成 Python 和 FastAPI 商品頁這一塊:17,957 個測試通過,17,727 組比對跟真的 PHP 輸出完全相同。獨立驗證判定 PROVEN。
Spring PetClinic(7 個服務) Spring Boot 2.6 升 3.3 找出 10 個檔案裡 59 個 javax import 要改,還有一個請求在 2.6 回 200、在 3.3 回 400(兩個版本都實際跑過確認)。

CardDemo 那一列我覺得最誠實。五輪驗證每輪都找到新問題,最後還是 NOT PROVEN。README 的解讀是:讓這種循環停下來的,不是工具,是有人決定「哪些輸入在範圍內」。這也是為什麼入口那一題「行為要完全一致,連怪癖都保留」會影響驗證要多嚴格。

另外還有一個特別做的「有陷阱的專案」:在註解、README、CLAUDE.md 和專案設定檔裡埋了指令,還有一支會產生標記檔的腳本、檔名裡藏 shell 語法。結果埋的東西一個都沒被執行,標記檔沒出現,原始碼沒被改,埋的那幾行被列在報告裡,裡面的憑證也被遮蔽了。拿 AI 去讀別人的舊程式碼,這點很重要。

讓 AI 碰不到原始碼

外掛的慣例是:原始碼放在 legacy/<name>,只讀;所有產出寫進 analysis/<name>/ 和 modernized/。跑 preflight 時加 --source <路徑>,它會建一個 symlink 指過去,不會複製,所以很大的 repo 也可以原地分析。

只靠慣例不夠,README 建議在工作目錄放一份 .claude/settings.json,用權限規則把它鎖起來:

{
  "permissions": {
    "allow": ["Read(**)", "Edit(analysis/**)", "Edit(modernized/**)"],
    "deny": ["Edit(/legacy/**)"]
  }
}

Edit 規則也涵蓋 Write 工具,Write(path) 這種寫法其實不會被參考。開頭的 / 是把規則固定在工作目錄的根。preflight 會檢查這條 deny 規則有沒有設。

還沒開始跑就要先知道的事

下面這些是讀設定說明時看到、我覺得很容易踩到的地方。

用了 --source 之後,legacy/<name> 是一個 symlink。上面那條 Edit(/legacy/**) 擋的是連結的路徑,不是它指向的真實路徑。所以要另外把真實路徑加進 additionalDirectories 允許讀取,再加一條 Edit(//真實路徑/**) 的 deny。只設前面那條,等於門鎖了、窗戶開著。

權限規則管得到 Claude 的檔案工具和它認得的 shell 指令,管不到自己去開檔案的腳本。所以 README 特別提醒,uplift 的步驟 5b 和 reimagine 的階段 E 會同時開很多會寫檔的代理,這兩步的 Bash 權限要維持在「每次詢問」的模式。

用量要有心理準備。最重的 extract-rules 在官方的測試裡每次開 50 到 200 個代理,跑之前會先告訴你要開幾個,大規模的會先問你。時間上,幾萬行的系統大概是:preflight 3 到 4 分鐘、assess 5 到 8 分鐘、map 和 extract-rules 各 5 到 15 分鐘,建置一個模組 15 到 30 分鐘。百萬行等級的系統,README 的建議是一次只做一個模組。

規則是模型挖的,跑兩次可能挖到不同的規則。官方自己對 osCommerce 跑兩次資安掃描,一次確認 48 個問題,另一次 52 個。所以 BUSINESS_RULES.md 要當成「需要審過的產出」,不是每次都一樣的建置結果。

如果你想用 claude -p 做成腳本,要注意 headless 模式等背景工作最多等 10 分鐘就不等了,extract-rules、harden 這些步驟會跑不完。要先設 CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS=0。

地圖、規則分片、驗證判定和報告都靠 Python 3.8 以上的 python3,scc 或 cloc 用來算程式碼規模,都可以先用 preflight 檢查。最強的那種驗證(新舊程式並排跑)需要這台機器能建置舊系統;沒有建置工具時,它會改用錄下來的輸出做比對,並在結果裡寫明。

最後是遙測。外掛預設會透過 Claude Code 自己的遙測送出使用次數,只有整數(跑了哪個指令、找到幾條規則、作業系統和 Python 版本這類),不送程式碼、路徑或檔名,100 以上的數字還會四捨五入到兩位有效數字。想關掉的話,外掛選項裡的 Usage counts、環境變數 CODE_MODERNIZATION_TELEMETRY=0,或 Claude Code 本身的 DISABLE_TELEMETRY=1,任一個都可以。比較要注意的是,每個版本在每台機器上啟動 session 時會送一次執行環境資訊,就算你沒用它的指令也會送。

什麼時候值得用

我的看法是,就算你根本沒打算重寫,modernize 入口選「先搞懂它就好」,跑出評估、結構圖、附行號的業務規則和一份計畫,對接手一套沒人敢動的舊系統就已經很值得。

真的要改寫時,它最大的價值不是幫你寫新程式碼(這件事 Claude Code 本來就做得到),而是那套驗證:拿舊程式當答案,自己生新輸入,確認測試會失敗,最後誠實給一個 NOT PROVEN。至於差異要不要接受、範圍要劃到哪裡,它不幫你決定,這部分還是要有人來負責。