# Anthropic 如何使用 Claude Code 進行大規模程式碼遷移

> 📖 本站完整內容索引（documentation index）：[llms.txt](/llms.txt)

> 原作者：ClaudeDevs (@ClaudeDevs) · 策展與摘要：EasyVibeCoding · 平台：X (Twitter) · 熱度：🔥🔥🔥🔥 · 日期：2026-07-22

> 原始來源：https://x.com/ClaudeDevs/status/2079654423828304282

## 中文摘要

# Anthropic 如何使用 Claude Code 進行大規模程式碼遷移

程式碼遷移（即將正式環境的程式碼庫移植到新語言的專案）在最近之前，都還需要耗時數年才能完成。

但在過去一個月中，Anthropic 的個別開發者運用 Claude Fable 5、Claude Opus 4.8 與動態工作流程，就成功遷移了 10 個程式庫，涉及數萬到數十萬行程式碼。

Bun 的共同創辦人兼 Anthropic 技術團隊成員 Jarred Sumner（@jarredsumner）使用 Claude Code 將 Bun 從 Zig 遷移到 Rust。在不到兩週的時間內產出了 100 萬行程式碼，在合併前，Bun 現有的測試套件在 CI 中的透過率達到了 100%。合併後浮現了 19 個迴歸問題，目前已全部修復。這個 Rust 版本已於 6 月在 Claude Code 內部上線。

Anthropic Labs 共同負責人 Mike Krieger（@mikeyk）用了一個週末的時間，將一個 Python 程式碼庫遷移為 165,000 行的 TypeScript。這其中包含了數百個 Agent、八個階段檢查點（phase gates）、三輪對抗式審查，以及最後的同等性檢查（parity check），將每個指令的輸出與原版 Python 進行 diff 比對。

Claude Code 的新功能徹底改變了這些長期被擱置的專案的成本效益評估。以下是我們目前採用、總結自這些遷移經驗的六個步驟流程。

核心見解是：你不用去修復程式碼，而是去修復產生程式碼的流程（迴圈）。

---

## 為什麼以及何時要進行語言遷移

團隊發起遷移的原因，在於初次建構與現今專案之間的環境變化。不是原先已知的取捨變成了瓶頸，就是出現了更好的作法，或者原本的生態系正在萎縮。

舉例來說，Jarred 當初選擇 Zig 是因為它兼具 C 語言等級的效能與極致的簡潔性，非常適合當時身為單人創辦人、「在 LLM 出現前奧克蘭一間狹窄公寓裡，花一年時間用 Zig 寫出 Bun」的他。這種簡潔性伴隨著已知的取捨，他曾在此處撰寫相關文章。

Bun 的 CLI 每月下載量已超過 1,000 萬次，並在 Claude Code 內部被廣泛使用。

就在上個季度，這些取捨還不足以構成凍結產品藍圖並投入資源進行數季專案的正當理由。你可能會維護兩個平行的程式碼庫數季甚至數年，而如果最終成果只有 90% 相容，你的頭痛問題反而比剛開始時更大。

現在，最壞的情況就是把這個分支刪掉重來。

我們依然需要合理的商業論證。雖然百萬行規模的遷移不再需要耗費 4 年專案期間內 300 萬至 400 萬美元的工程資源，但執行起來仍然需要花費數萬到數十萬美元不等。例如，Bun 的遷移消耗了 59 億個未快取的輸入 token 和 6.9 億個輸出 token，以 API 定價計算大約是 165,000 美元。Mike 移植工作的主要部分則消耗了 2,700 萬個 token。

![這張圖片顯示了一個名為「Rewrite Bun in Rust #30412」的軟體專案合併請求（Pull Request）介面，其中包含程式碼變更與提交紀錄。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/cfdc86e3e107eb33.png)

<details class="chart-data"><summary>展開畫面重點</summary><div class="me-note">畫面為某版本控制平台的 Pull Request 介面，標題為「Rewrite Bun in Rust #30412」，狀態標示為「Merged」，由 Jarred-Sumner 將 6755 個 commits 合併至 `main` 分支（來源為 `claude/phase-a-port`），時間為 May 14。
上方統計數據顯示：Conversation 1127、Commits 6755、Checks 7、Files changed 2188，整體程式碼變更量為 +1,009,257, -4,024。
下方選取了 `test/js/bun/spawn/spawn.test.ts` 檔案的 Commit `68a34bf` 變更細節：
- 提交者：`dylan-conway` 於 May 13 提交，標記為 Verified。
- 程式碼差異（Diff）內容：
  - 移除部分：移除了原本等待子進程結束的邏輯，包括 `// Wait for the child to exit before reading stdout...` 的註解以及 `await proc.exited;`。
  - 新增部分：改為 `await Bun.sleep(1);`。
  - 下方保留了 `const out = await proc.stdout.text();` 與 `expect(out).not.toBe("");`。</div></details>

然而，遷移的理由不再需要生死攸關。更新日誌中長達一年的記憶體臭蟲修復紀錄，或是一個長期的效能瓶頸，現在就足以成為遷移的理由。

編譯步驟是 Mike 啟動專案的契機。他團隊內部維護的工具會打包成單一二進位檔發布給使用者。透過 Python 工具鏈產生該二進位檔每個平台大約需要 8 分鐘，在每次發布的建構矩陣（build matrix）中累積起來總共要等待 30 分鐘。遷移之後，同樣的編譯現在只需大約 2 秒，二進位檔啟動速度快了 6 倍，團隊也得以退役一套獨立的部署管道。

---

## 為什麼 AI 會改變程式碼遷移的成本效益計算

Fable 和 Opus 4.8 非常擅長利用子 Agent 來委派、指揮和驗證平行的工作串流，同時找出通往既定目標的多條路徑。

大型程式碼遷移是這些先進模型特別有效的應用場景，原因如下：

- 工作是平行的。工作可以分派到數千個獨立的單元（例如檔案和 crate）同時執行，因此 Agent 可以同步工作，而不必互相等待。

- context 清晰且全面。舊程式碼是模型的絕佳規格說明書。

- 內建裁判。許多大型程式碼庫都包含測試套件，Agent 可以用來驗證它們的工作成果。

- 佇列會自動產生。當編譯器或測試執行失敗時，就會變成下一個需要 Agent 修復的項目。

- 它們需要一致性與邊界案例處理：審查者會引述每個發現背後的規則，因此違規事項會變成佇列項目，而不是悄悄出現的分歧。

---

## 大規模程式碼遷移的六個步驟

如需更多細節，你可以閱讀 Jarred 的部落格。

前置準備

在開始遷移專案之前，必須先建立一個強而有力的裁判，否則你將無法界定結束條件或衡量成功與否。

要建立這個裁判：

- 將現有測試分類。使用 Claude 識別哪些測試可以表達為外部呼叫，哪些測試依賴於無法移植的內部實作。

- 為了可移植性進行重構。將面向外部的測試轉換為可以同時針對原始版本與移植版本執行的斷言。使用對抗式 Agent 來驗證重構後的測試沒有削弱斷言的嚴格度。

- 驗證裁判。針對原始程式碼執行它，以確認能順利通過。接著針對刻意寫壞的程式碼執行它，以確認會失敗——無法捕捉損壞的裁判就不能稱作裁判。

這大致遵循了 Jarred 的方法論，並在每個階段設置審查與檢查點。Mike 採用了類似的整體結構與迴圈工作流程，但他端到端地執行了整個遷移過程，根據結果修正規則與工作流程，然後再次執行——每一次都丟棄產出，直到第三次執行才保留。

![這是一張詳細的工程架構流程圖，展示了一位工程師在迴圈之外，透過 6 個步驟與共用文件來協調多個代理程式進行自動化開發與錯誤修正的系統設計。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/78c77320ccab35b7.jpg)

<details class="chart-data"><summary>展開畫面重點</summary><div class="me-note">One engineer, outside the loop
reads outputs, edits the loop, gates phases — "prompting Claude to edit the loop to fix things"
teal = does the work · coral = checks it · amber = shared documents

Step 1 · create the map and the rules
output: trusted map + rules
- Rulebook authors (each decision once) [teal]
- Dependency mappers (order the work) [teal]
- Gap inventory (trace the control flow) [teal]
- Rule auditors (one mistake class each) [coral]
- Skeptic reviewers x2 (attack each entry) [coral]
- Joint audit (inventory + rules agree) [coral]

Step 2 · stress-test the rules
output: hardened rules
- Dual translators x2 (same files, separate contexts) [teal]
- Diff inspector (diffs indict rules) [coral]
- Pilot run (rehearse on select files) [teal]

Step 3 · translate everything
queue: the file list
- Implementers (one per file) [teal]
- Reviewers x2 (assume it's wrong) [coral]
- Fix agents (apply confirmed fixes) [teal]

Step 4 · compile
queue: compiler errors
- Survey build x1 (one build grades all) [teal]
- Parallel fixers (no compiler access) [teal]
- Tiebreaker review (default: not confirmed) [coral]

Step 5 · run it
queue: deduped crashes
- Smoke tests (surface the crashes) [teal]
- Fixers per cause (group by cause) [teal]
- Reviewers x2 (verify each fix) [coral]

Step 6 · match behavior
queue: failing tests -&gt; merge
- Build daemon x1 (owns the only rebuild) [teal]
- Fixers x30 (read-only evidence) [teal]
- Triage lookup (rules, not judgment) [coral]

↺ a repeated miss → one sentence edited in the rules → the batch regenerates

Shared documents · the rules outlive the code
- Dependency map (the work, in order) [amber]
- Rulebook (read before every task) [amber]
- Gap inventory (for other Claudes to read) [amber]
- Original code (kept beside, as spec) [amber]
- Trap + skip lists (grow round over round) [amber]
- Triage ledger (slow tests, no commits) [amber]
- Machine queues (errors, crashes, fails) [amber]

Run infrastructure · cheats stopped by mechanism
4 worktrees x 16 agents = 64 Claudes · git, cargo and slow commands banned in-loop</div></details>

---

## 步驟 1 — 建立規則手冊、相依性對應圖與差距清單

順序至關重要：規則手冊必須在差距清單之前建立。差距清單是由規則手冊的預設值所無法涵蓋的部分所定義，兩者會在聯合稽核中一同接受測試。

規則手冊

規則手冊的確切樣貌取決於你在開始時必須做出的關鍵架構決策。其中最重要的是：新程式碼是要遵循相同的結構，還是要進行完全重新設計。

如果是前者（像 Jarred），規則手冊主要會是查詢表，用於在不同語言之間轉換型別與慣用語，同時將較難轉換的元件指向差距清單。如果是後者（像 Mike），它就會是一份設計文件。

Jarred 透過與 Claude 對話來建立他的規則手冊，為每個模稜兩可的領域制定政策。基於他自己的直覺，他還使用了 8 個專門設計用於審查 8 種常見失敗模式類別的子 Agent。

相依性對應圖

為了有效地將工作串流拆分進行平行遷移，你需要了解檔案相依性，以便知道哪些檔案應該優先遷移、哪些檔案應該包含在同一個批次中。Claude Code 可以部署 Agent 來建立並執行一個確定性的指令碼來產生這個對應圖。

差距清單與質疑型審查者

新語言有著不同於舊語言、必須被滿足的需求。對 Zig 來說，遷移到 Rust 的差異在於手動記憶體管理（C 與 C++ 的運作方式相同）。例如：

```markdown
// Zig

fn readConfig(allocator: std.mem.Allocator) ![]u8 {
    const buf = try allocator.alloc(u8, 1024);
    // ...fill buf...
    return buf; // caller must free this — but only the comment says so
}

// A caller that forgets 'defer allocator.free(buf)' still compiles — the leak only surfaces at runtime.

```

```rust
fn read_config() -> Vec<u8> {
    let buf = vec![0u8; 1024];
    // ...fill buf...
    buf // ownership moves to the caller; memory is freed automatically
}

// Use it after it's moved? Free it twice? Neither compiles.
// Forget to free it? There's no free call to forget — drop is automatic.

```

從 Python 到 TypeScript 的差距則是介面與合約（contracts）。Python 不需要宣告它會接受什麼形狀的物件或傳回什麼的合約，但 TypeScript 需要。

Jarred 和 Mike 都建立了捕捉這些隱含知識的差距清單檔案。Jarred 在前期就將這些差距列入清單中（這也是我們在這裡的做法），而 Mike 則選擇先進行翻譯，然後透過事後稽核來建立差距清單。你可能兩者都需要做。

請參考這個用來建立差距清單檔案的 Claude Code 範例 Prompt。

---

## 步驟 2 — 對規則進行壓力測試

![這張流程圖展示了流程中的「步驟 2：壓力測試規則」（Step 2 · stress-test the rules），包含雙重翻譯器、差異檢查器與試運行三個核心環節。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/567872d7cc70a04d.png)

<details class="chart-data"><summary>展開畫面重點</summary><div class="me-note">圖片為一個流程區塊，標題為：
Step 2 · stress-test the rules
右上角標註：
output: hardened rules

內部包含三個並排的步驟方框：
1. 左側方框（綠色）：
- 標題：Dual translators ×2
- 說明：same files, separate contexts

2. 中間方框（橘紅色）：
- 標題：Diff inspector
- 說明：diffs indict rules

3. 右側方框（綠色）：
- 標題：Pilot run
- 說明：rehearse on select files</div></details>

在這個步驟中，Jarred 使用一個 Agent 根據規則手冊翻譯三個檔案，一個 Agent 「像資深 Rust 工程師一樣」翻譯三個檔案，還有一個 Agent 利用 diff 來建立新的翻譯規則。在這個階段，他抓出了兩個關鍵問題，如果這些問題擴散到全部 1,448 個檔案中，將會引發無數的問題。

這種壓力測試只適用於結構保留型遷移（structure-preserving migrations），在這種遷移中，同一個檔案的兩次翻譯可以進行逐行比較。如果你的規則手冊是一次重新設計（如 Mike 的作法），對應的測試就是直接用對抗式審查者來攻擊設計文件，然後用一次性的端到端執行來驗證它。

無論如何，請把翻譯出來的檔案全部丟棄。目標是精煉規則，而不是取得漸進式的進展。

---

## 步驟 3 — 翻譯所有內容

![這張圖展示了一個名為「Step 3 · translate everything」的工作流程步驟，包含實作、審查與修正代理程式的分配與任務。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/c505cb52d9817fac.png)

<details class="chart-data"><summary>展開畫面重點</summary><div class="me-note">圖片內容為工作流程中的第三步驟，標題為「Step 3 · translate everything」，右上角註明「queue: the file list」。
下方並列三個區塊：
1. 「Implementers」（one per file），背景為綠色系。
2. 「Reviewers ×2」（assume it's wrong），背景為橘紅色系。
3. 「Fix agents」（apply confirmed fixes），背景為綠色系。</div></details>

在後續的步驟中，你將執行相同的多 Agent 迴圈架構：實作、審查與修復。

你可以將實作者的工作卸載給較小的模型，並讓審查者使用較大的模型。例如，Mike 在為主要遷移派發 12 個子 Agent 時，就使用了 Claude Sonnet。

工作佇列應該是機械化的。批次指令碼透過檢查磁碟上是否存在已翻譯的檔案來決定哪些工作已完成，然後將待處理的檔案切分批次分派給實作者 Agent。由於佇列每次都會從磁碟重新建構，因此這項遷移天生就是可恢復的（resumable）。

任何翻譯器無法滿懷信心執行的部分，都會被標記為 `// TODO(port): <reason>`，以便在步驟 4 中處理。

兩個對抗式審查者會使用各自獨立的 context 來評估實作者的工作，若審查者意見分歧則會提交給第三個 Agent。當審查者不斷在不同檔案中發現同一個錯誤時，修復方式不應該是逐個檔案修改。你需要在規則手冊中增加一句話，然後重新產生受影響的批次。規則手冊會在這個步驟中不斷成長；程式碼絕對不會針對它手動打補丁。

這個步驟中需要注意的一個重要架構決策是編譯器放置的位置。Mike 在每個迴圈內都執行了 TypeScript 編譯器，因為它能在幾秒鐘內檢查完一個單元。Jarred 則完全禁止編譯器進入迴圈，並將其推遲到下一個步驟，因為 Cargo 需要花費好幾分鐘。

---

## 步驟 4、5、6 — 編譯、執行並比對行為

![這張圖片展示了一個軟體工程工作流程的三個步驟（Step 4 至 Step 6），每個步驟包含不同的執行任務與隊列分類。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/e4dbac47e5b0e30e.png)

<details class="chart-data"><summary>展開畫面重點</summary><div class="me-note">這張圖表列出了工作流程中的最後三個步驟：

1. **Step 4 · compile**（queue: compiler errors）
   - **Survey build ×1**: one build grades all（淡綠色區塊）
   - **Parallel fixers**: no compiler access（淡綠色區塊）
   - **Tiebreaker review**: default: not confirmed（淡紅色區塊）

2. **Step 5 · run it**（queue: deduped crashes）
   - **Smoke tests**: surface the crashes（淡綠色區塊）
   - **Fixers per cause**: group by cause（淡綠色區塊）
   - **Reviewers ×2**: verify each fix（淡紅色區塊）

3. **Step 6 · match behavior**（queue: failing tests → merge）
   - **Build daemon ×1**: owns the only rebuild（淡綠色區塊）
   - **Fixers ×30**: read-only evidence（淡綠色區塊）
   - **Triage lookup**: rules, not judgment（淡紅色區塊）</div></details>

這三個步驟共用相同的迴圈架構，並且對人類判斷力的需求逐漸減少，因此我們將它們放在一起介紹。

Jarred 是透過一個協調器指令碼來執行此操作，該指令碼在整個 workspace 中呼叫了一次編譯器。接著，「修復 Agent」會與對抗式審查並行地遍歷錯誤清單。建構過程再次執行，如此重複循環。

審查錯誤清單有助於捕捉可能需要調整的系統性問題。例如，Jarred 遇到了數千個 Rust 模組錯誤，這些錯誤是在修復了 Zig 的延遲編譯（lazy compilation）所容許的循環匯入後才浮現的。他透過在迴圈中編碼邏輯來修正這個問題，該邏輯能將相依性分類，決定是要刪除、移動還是重構其邊界。

步驟 5 也擁有一個類似編譯器錯誤清單的機械化事實來源（source of truth）：冒煙測試（smoke test）所產生的崩潰。同樣地，迴圈的修復方式是將問題分門別類，在此情況下，是根據根本原因將問題分組，並由對抗式子 Agent 進行審查。

步驟 6 也是我們故事的尾聲，即跨越兩個程式碼庫比對程式的行為。

我們的檔案現在已經經過翻譯、編譯和冒煙測試。

現在是時候將它們分片（sharded），並對其執行測試套件（來自前置準備階段）了。利用「修復 Agent」來處理失敗案例，這些 Agent 會針對兩個程式碼庫審查失敗的測試。對抗式審查者則會檢查他們的修復成果。

這個迴圈中的下一個階段是建構常駐程式（build daemon），這是唯一被允許重新建構二進位檔的行程。修復者編寫補丁；常駐程式將其打包成批次、重新建構一次、重新執行受影響的測試，並將結果回饋回來。這將最昂貴的操作序列化，而不是讓多個 Agent 獨立觸發它。

Mike 的方法在這裡相當重要，因為許多開發者可能沒有現成且已移植的測試套件。Mike 讓 Claude 建立了一個小型指令碼，針對新移植版本與原始 Python 程式碼庫執行 7 個真實世界的場景，並對比輸出結果（diff）。每個失敗的場景都有專屬的修復 Agent，迴圈持續執行直到所有 7 個場景全部通過。

接著他更進一步。Claude 自主設計了自己的端到端測試套件，並在夜間自主執行，修復出錯的部分，連續四個晚上不斷重複。結果，它捕捉到了任何場景清單都無法預測的小問題（paper cuts）。

這個教訓是：缺少測試套件並不會阻礙這個步驟。如果你無法繼承現有的裁判，就讓 Claude 建立一個。無論如何，你的原始程式碼庫就是唯一的絕對基準（ground truth）。

---

## 程式碼遷移最佳實踐

每次執行都教會了我們前一次不知道的事。但有幾項實踐在每個專案中都經受住了考驗：

- 不要盲目遵循本指南。每一次遷移都是獨一無二的。請將此視為起點，並在投入之前與 Claude 一起規劃你專屬的遷移方案。

- 不要專注於單一的失敗。單一失敗是迴圈的工作。你的注意力應該放在模式上。

- 讓審查保持對抗性，讓驗證保持機械化。讓指令碼（編譯器、diff、測試套件）來擔任裁判。

- 不要把最大的模型用在所有事情上。較小的模型足以應付高流量的實作派發；把最大的模型留給審查者，以及任何需要編寫供其他 Agent 遵循的規則的任務。

- 把人類的時間花在前面。規則手冊和壓力測試是最耗時的部分。之後的一切基本上都是在消耗佇列。

---

## 審查迴圈的結果，而不是程式碼

Jarred 的 Bun 遷移現在已經上線營運，雖然任何遷移都有其取捨。例如，大約 4% 的 Rust 程式碼位於 `unsafe` 區塊內，主要是 C/C++ 邊界處的單行指標操作。

但新的程式碼庫在各方面都明顯更好。團隊工具能夠偵測到的每一個記憶體流失都已經被修復：一項 2,000 次重複建構的基準測試中，記憶體用量從 6,745 MB 大幅降至 609 MB。在 Linux 和 Windows 上，二進位檔的體積縮小了 19%。跨語言最佳化更使得 HTTP 服務以及諸如 `next build` 和 `tsc` 等真實工作負載的效能提升了 2% 到 5%。

挑選一個你一直以來在將就的程式碼庫，問問 Claude 針對它進行遷移的流程會是什麼樣子。

## 標籤

Claude Code, CLI, IDE, 自動化, 功能更新, Anthropic, Claude
