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

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

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

> 原始來源：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 萬行的程式碼，並且在合併前，CI 中 Bun 現有的測試套件通過率達到 100%。合併後浮現了 19 個回歸問題，且全都獲得修復。這個 Rust 版本已於 6 月份在 Claude Code 內部上線。

Anthropic Labs 共同負責人 Mike Krieger（@mikeyk）在一個週末內，將一個 Python 程式碼庫遷移成了 165,000 行的 TypeScript。這其中包含了數百個 Agent、八個階段檢查點（phase gates）、三輪對抗式審查，以及最後的差異比對（diff）檢查，用來比對每個指令的輸出與原版 Python 是否一致。

Claude Code 的新功能徹底改變了這些長期擱置專案的成本效益計算。以下是我們現在採用的六步驟流程，這些經驗都是從上述遷移實踐中學到的。

核心見解在於：你不用去修復程式碼，而是去修復產生程式碼的流程（迴圈）。

---

## 為什麼以及何時該遷移語言

團隊之所以發起遷移，是因為最初建構時的環境與當前專案的現狀有所落差。要么是過去已知的取捨變成了瓶頸、出現了更好的方法，要么是原本的生態系正在萎縮。

舉例來說，Jarred 當初選擇 Zig 是因為它兼具 C 語言等級的效能與極致的簡潔性，非常適合身為獨行創辦人的他在「LLM 出現前，在奧克蘭一間狹窄的公寓裡花一年時間寫出 Bun」。這種簡潔性伴隨著已知的取捨，他曾在這裡寫下相關心得。

Bun 的 CLI 每月下載量已超過 1,000 萬次，並在 Claude Code 內部被廣泛使用。

就在上個季度，這些取捨還不足以構成凍結產品藍圖並投入資源進行數季專案的正當理由。你可能會維護兩套平行的程式碼庫數季甚至數年，而如果最終成果只有 90% 的相容度，帶給你的麻煩反而比剛開始時更大。

現在，最壞的情況就是把這個分支刪掉重來。

當然，仍然需要具備合理的商業價值。雖然百萬行等級的遷移不再需要耗費四年專案、價值 300 萬到 400 萬美元的工程資源，但執行起來仍然需要花費數萬到數十萬美元不等的成本。以 Bun 的遷移為例，它消耗了 59 億個未快取的輸入 token 和 6.9 億個輸出 token — 按照 API 定價大約是 165,000 美元。Mike 移植專案的主要部分則消耗了 2,700 萬個 token。

![GitHub 上的 Pull Request 畫面，展示了「Rewrite Bun in Rust #30412」合併提交的程式碼變更。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/cfdc86e3e107eb33.png)

<details class="chart-data"><summary>展開畫面重點</summary><div class="me-note">畫面為 GitHub 上的 Pull Request 頁面，標題為「Rewrite Bun in Rust #30412」。
頂部顯示該 PR 已由 `Jarred-Sumner` 於 5 月 14 日合併 6755 個 commit，從 `claude/phase-a-port` 合併至 `main`。
右上方顯示檔案變更統計：`+1,009,257 -4,024`。
下方顯示 Commit `68a34bf` 的詳細資訊，由 `dylan-conway` 於 5 月 13 日提交（已驗證），標題為：`test: revert proc.exited change in spawn.test.ts, keep isDebug iteration count`。
程式碼差異對照（diff）檔案為 `test/js/bun/spawn/spawn.test.ts`，變更內容如下：
刪除（紅色）：
- `// Wait for the child to exit before reading stdout. Using a real`
- `// signal (process exit) instead of a fixed timer keeps this`
- `// deterministic: it exercises "stdout is fully readable after the`
- `// child has exited" without depending on timing.`
- `await proc.exited;`

新增（綠色）：
- `await Bun.sleep(1);`

後續程式碼：
- `const out = await proc.stdout.text();`
- `expect(out).not.toBe("");`
- `}`</div></details>

然而，遷移的理由已不再需要生死攸關。只要有changelog 中累積了一年的記憶體錯誤修復，或是遇到某個長期的瓶頸，現在就足以成為發起遷移的理由。

編譯步驟就是促成 Mike 專案的契機。他的團隊所維護的內部工具是以單一二進位檔（binary）的形式發佈給使用者。使用 Python 工具鏈來產生這個二進位檔，每個平台大約需要 8 分鐘，在每次發佈的建構矩陣（build matrix）中總共需要等待 30 分鐘。遷移之後，同樣的編譯現在大約只需要兩秒鐘，二進位檔的啟動速度快了 6 倍，團隊也因此能夠除役一套獨立的部署管道。

---

## 為什麼 AI 改變了程式碼遷移的數學公式

Fable 和 Opus 4.8 特別擅長透過子 Agent 來委派、指揮與驗證平行的工作串流，同時能找出通往既定目標的多條路徑。

大型程式碼遷移之所以是這些先進模型的絕佳應用場景，原因在於：

- 工作具備平行性。工作可以分散在數千個獨立單位（例如檔案和 crate）中同時執行，因此 Agent 可以同步工作，而不必互相等待。

- context 清晰且全面。舊程式碼是模型的絕佳規格說明書。

- 具備內建的裁判。許多大型程式碼庫都包含測試套件，Agent 可以用來驗證它們的工作成果。

- 佇列會自動產生。當編譯或測試執行失敗時，該失敗項目就會變成 Agent 下一個要修復的任務。

- 它們需要一致性與邊界案例處理：審查者會引述每個發現背後的規則，因此違規事項會變成佇列項目，而不是默默存在的程式碼分歧。

---

## 大規模程式碼遷移的六個步驟

如需更多細節，你可以閱讀 Jarred 的部落格。

先決條件

在開始遷移專案之前，首要條件是建立一個強而有力的裁判機制，否則你將無法界定結束條件或衡量成功與否。

要建立這個裁判機制：

- 將現有的測試分類。使用 Claude 來識別哪些測試可以表達為外部呼叫，哪些則依賴無法移植的內部實作。

- 為了可移植性而重寫。將面向外部的測試轉換為可以針對原始版本和移植版本同時執行的斷言（assertions）。使用對抗式 Agent 來驗證重寫後的測試沒有削弱斷言的嚴格性。

- 驗證裁判機制。針對原始程式碼執行它，以確認能夠通過。然後針對故意破壞的程式碼執行它，以確認會失敗 — 一個抓不出破壞的裁判根本不能算是裁判。

這基本上遵循了 Jarred 的方法論，並在每個階段進行審查與把關。Mike 採用了類似的整體結構與迴圈工作流程，但他以端到端的方式執行了整個遷移，根據結果修正規則與工作流程，然後再次執行 — 每次都丟棄輸出結果，直到第三次執行才採用。

![這是一份展示單一工程師透過六個步驟搭配多代理人架構來管理與執行專案的流程圖。](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 ×2: attack each entry (coral)
  - Joint audit: inventory + rules agree (coral)

- Step 2 · stress-test the rules (output: hardened rules)
  - Dual translators ×2: 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 ×2: assume it's wrong (coral)
  - Fix agents: apply confirmed fixes (teal)

- Step 4 · compile (queue: compiler errors)
  - Survey build ×1: 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 ×2: verify each fix (coral)

- Step 6 · match behavior (queue: failing tests → merge)
  - Build daemon ×1: owns the only rebuild (teal)
  - Fixers ×30: 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 (amber 區塊)
  - Dependency map: the work, in order
  - Rulebook: read before every task
  - Gap inventory: for other Claudes to read
  - Original code: kept beside, as spec
  - Trap + skip lists: grow round over round
  - Triage ledger: slow tests, no commits
  - Machine queues: errors, crashes, fails

- Run infrastructure · cheats stopped by mechanism
  - 4 worktrees × 16 agents = 64 Claudes · git, cargo and slow commands banned in-loop</div></details>

---

## 步驟 1 — 建立規則手冊、相依性對應圖與差距清單

順序很重要：規則手冊必須在差距清單之前建立。差距清單是由規則手冊的預設值無法涵蓋的部分所定義，兩者會在聯合稽核中一同接受測試。

規則手冊

規則手冊的確切樣貌取決於你在開始時必須做出的關鍵架構決策。其中最重要的是，新程式碼要遵循相同的結構，還是要完全重新設計。

如果是前者（如 Jarred），規則手冊主要會是查閱表格，負責在不同語言之間轉換型別與慣用法，同時針對較難轉換的元件指向差距清單。如果是後者（如 Mike），它就會是一份設計文件。

Jarred 透過與 Claude 對話來建立他的規則手冊，針對每個模糊不清的領域制定方針。他還使用了八個專門設計的子 Agent，根據他自己的直覺，針對 8 種常見失敗模式的類別進行審查。

相依性對應圖

為了有效地為平行遷移拆分工作串流，你必須理解檔案的相依性，以便知道哪些檔案應該優先遷移，以及哪些檔案應該包含在同一個批次中。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 而言，差距則在於介面與合約。Python 不需要宣告它會接受什麼形狀的物件或會回傳什麼的合約，但 TypeScript 需要。

Jarred 和 Mike 都建立了捕捉這些隱性知識的差距清單檔案。Jarred 在前期就將這些差距列入清單中（這也是我們在此處的做法），而 Mike 則選擇先進行轉換，然後透過事後稽核來建立差距清單。你可能需要兩者並行。

請參考以下用來建立差距清單檔案的 Claude Code 樣本 Prompt。

---

## 步驟 2 — 對規則進行壓力測試

![流程圖展示工作流程的第二步驟「壓力測試規則」，包含多個驗證與執行環節。](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
- 第一個方塊（淡綠色）：
  - 標題：Dual translators ×2
  - 副標題：same files, separate contexts
- 第二個方塊（淡紅色）：
  - 標題：Diff inspector
  - 副標題：diffs indict rules
- 第三個方塊（淡綠色）：
  - 標題：Pilot run
  - 副標題：rehearse on select files</div></details>

在此步驟中，Jarred 使用了一個 Agent 透過規則手冊轉換三個檔案、一個 Agent「像資深 Rust 工程師一樣」轉換三個檔案，以及一個 Agent 利用 diff 來建立新的轉換規則。在這個階段，他抓出了兩個關鍵問題，如果將其擴散到全部 1,448 個檔案中，將會引發無數的問題。

這種壓力測試僅適用於結構保留型遷移（structure-preserving migrations），在這種遷移中，同一個檔案的兩種翻譯可以逐行進行比較。如果你的規則手冊是一次重新設計（就像 Mike 的做法），對應的測試就是直接用對抗式審查者來攻擊設計文件，然後透過一次性的端到端執行來進行驗證。

無論如何，請把所有轉換後的檔案丟棄。目標是精進規則，而不是取得漸進式的進展。

---

## 步驟 3 — 轉換所有內容

![流程圖展示工作流程的第三步驟「translate everything」，包含三個並行的代理角色：Implementers、Reviewers 與 Fix agents。](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 — 編譯、執行並比對行為

![軟體開發工作流程的詳細步驟圖解，包含編譯、執行與行為比對等三個階段與多個處理模組。](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/e4dbac47e5b0e30e.png)

<details class="chart-data"><summary>展開畫面重點</summary><div class="me-note">圖片展示了軟體開發與測試工作流程中的三個連續步驟，分別為 Step 4、Step 5 與 Step 6：

- **Step 4 · compile**（queue: compiler errors）
  - **Survey build ×1**: one build grades all
  - **Parallel fixers**: no compiler access
  - **Tiebreaker review**（紅色標籤）: default: not confirmed

- **Step 5 · run it**（queue: deduped crashes）
  - **Smoke tests**: surface the crashes
  - **Fixers per cause**: group by cause
  - **Reviewers ×2**（紅色標籤）: verify each fix

- **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），這是唯一被允許重新建構二進位檔的行程。修復者負責編寫修補檔（patches）；常駐程式將其打包成批次、執行一次重新建構、重新執行受影響的測試，並回饋結果。這將最昂貴的操作序列化，而不是讓多個 Agent 獨立觸發它。

Mike 的方法在這裡至關重要，因為許多開發者並不會擁有一個現成或已經移植好的測試套件。Mike 讓 Claude 建立了一個小型指令碼，針對新的移植版本與原版的 Python 程式碼庫執行 7 個真實世界的情境，並比對其差異。每個失敗的情境都有其專屬的修復 Agent，迴圈會一直執行到全部 7 個情境都通過為止。

接著他更進一步。Claude 自行設計了一套端到端測試套件，並在夜間自主執行，修復損壞的部分，連續四個晚上不斷重新執行。因此，它捕捉到了任何情境清單都無法預測到的微小細節（paper cuts）。

這給我們的教訓是：缺少測試套件並不會阻礙這個步驟。如果你無法繼承現有的裁判，就讓 Claude 自己打造一個。無論如何，你的原始程式碼庫就是唯一的真實來源。

---

## 程式碼遷移最佳實務

每一次的執行都教會了我們前一次不知道的事。但有幾項實務做法在每個專案中都經受住了考驗：

- 不要盲目遵循本指南。每一次的遷移都是獨一無二的。請將此視為起點，並在commit 之前先與 Claude 一起規劃你專屬的遷移方案。

- 不要將焦點放在單一失敗上。單一失敗是迴圈的工作。你的注意力應該放在模式上。

- 讓審查保持對抗性，並讓驗證保持機械化。讓指令碼（編譯器、diff、測試套件）來擔任裁判。

- 不要把所有事情都用最大的模型來做。較小的模型足以應付高容量的實作擴散（fan-out）；請將你最大的模型留給審查者，以及任何負責編寫其他 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, 功能更新, Agent, LLM, Anthropic, Bun
