Migrating Millions of Lines of Code with Claude Code: 6 Essential Steps
The article details how Anthropic engineers used Claude Code and multiple AI agents to migrate large codebases—from a million‑line Zig project to Rust and a 165k‑line Python codebase to TypeScript—outlining a six‑step workflow, cost considerations, and practical principles for AI‑assisted large‑scale code migration.
In recent months Anthropic demonstrated that AI can dramatically accelerate cross‑language code migration, moving from years of manual effort to weeks or days by leveraging Claude Code and a fleet of specialized agents.
Two flagship migrations are highlighted: Bun’s founder Jarred Sumner rewrote roughly 1 million lines of Zig code to Rust in 11 days , submitting a PR that passed Bun’s existing CI suite after fixing 19 known regressions; the Rust version now powers Claude Code v2.1.181 and later releases. Separately, Anthropic Labs co‑lead Mike Krieger transformed a 165 k‑line Python codebase into TypeScript over a weekend, using hundreds of agents, eight acceptance checkpoints, and three rounds of adversarial review to ensure behavioral parity.
While AI lowers migration cost, the effort remains expensive. The Bun migration consumed 5.9 billion input tokens and 690 million output tokens , estimated at about $165 k in API fees; Mike’s migration used roughly 27 million tokens . Overall projects can cost $300 k–$4 M, but the payoff includes reduced memory usage, smaller binaries, and 2‑5% performance gains in real workloads.
The article explains why AI fits large‑scale migration: tasks can be split into parallel units; the original code provides complete context; compilers, test suites, and diffs act as judges; failures become queued tasks; and a unified rulebook enforces consistent behavior.
Six‑Step Migration Workflow
Before starting, teams must establish a reliable acceptance system that serves as the migration “judge.” Building this system involves:
Classifying existing tests and identifying which can run via CLI/API versus those tied to the old language.
Rewriting external‑behavior tests into a unified set of assertions usable for both old and new implementations.
Having a dedicated review agent audit the rewritten tests to ensure requirements are unchanged.
Running the full acceptance suite on the original code to confirm baseline correctness.
Intentionally corrupting code to verify the suite detects errors.
Ensuring the suite cannot miss obvious problems before it is trusted for migration validation.
Anthropic then creates three preparatory artifacts:
Rulebook : language‑mapping tables or architectural design documents that define how each construct should be translated.
Dependency graph : determines file/module ordering and parallel batch groups, generated by Claude Code scripts.
Gap list : records language‑specific features that lack direct mapping (e.g., Zig’s manual memory release vs. Rust’s ownership).
During a pressure‑test phase, two agents independently translate a sample set of files; a third agent diffs the results and expands the rulebook. This “dual‑translation‑compare” approach uncovers systematic errors before scaling to the full codebase.
Full migration proceeds in a loop:
Implementation agents generate code for a batch.
Independent review agents audit the output.
Disagreements are resolved by a third adjudicating agent.
Compiled code is built; errors are turned into TODO(port): <reason> tasks for further agents.
Human judgment is concentrated early—refining the rulebook and handling recurring issues—while later stages rely on automated compilation, runtime checks, and test verification. Key practical principles distilled from multiple projects include:
Start with a migration plan derived from the codebase’s gap analysis.
Prioritize fixing recurring failures over one‑off bugs.
Combine adversarial review with automated verification using compilers, static checks, and diffs.
Assign smaller models to high‑throughput implementation work and larger models to rule design and critical decision‑making.
Front‑load human effort on rule creation and pressure testing; let agents handle the bulk of repetitive translation and fixing.
Quality is measured by concrete metrics: after Bun’s migration, memory usage dropped from 6,745 MB to 609 MB, binary size shrank by 19%, and real‑world workloads (e.g., next build , tsc ) saw 2‑5% speed improvements. Such data, alongside behavior‑consistency tests, determine the success of a migration, not merely the line count generated. Relevant resources are listed at the end, including the Claude Code Migration Kit on GitHub and the original blog posts describing the migrations. <code>fn loadConfig(allocator: std.mem.Allocator) ![]u8 { const data = try allocator.alloc(u8, 1024); // fill data return data; // caller must free } </code> <code>fn load_config() -> Vec<u8> { let data = vec![0u8; 1024]; // fill data data // ownership transferred, freed automatically } </code>
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
