Anthropic Claude Code Migration Took Bun From Zig to Rust in Two Weeks, but Speed Wasn't the Hard Part
- Ethan Carter

- Jul 17
- 12 min read
Updated: Jul 20
Anthropic used Claude Code to migrate Bun from Zig to Rust, producing roughly one million lines in less than two weeks. The Anthropic Claude Code migration passed Bun’s existing continuous integration tests before merging. Yet 19 regressions still reached the merged codebase.
That contrast matters more than the raw output. Claude Code generated code at a pace no conventional engineering team could match. However, the project succeeded only after humans built strict rules, isolated reviewers, mechanical work queues, and language-independent tests.
The real contest is therefore not AI agents versus human programmers. It is automated code generation versus automated verification. Anthropic’s results suggest generation is becoming cheap and abundant, while trustworthy acceptance remains the limiting engineering problem.
Bun co-founder Jarred Sumner ran the migration after Bun joined Anthropic in December 2025. He used a pre-release Claude model and approximately 50 dynamic workflows over 11 days. Anthropic later turned lessons from that work into a general migration process.
The result challenges one of software engineering’s oldest warnings: avoid complete rewrites whenever possible. That warning still has force, but the cost model behind it has changed.
The Anthropic Claude Code Migration Rewrote Bun at Machine Scale
Anthropic compressed a project once measured in engineer-years into an 11-day agent workflow, without freezing Bun’s normal development for a year.
Bun is a JavaScript and TypeScript runtime that also includes a package manager, test runner, bundler, and compatibility layer for Node.js APIs. Its broad scope made a language migration unusually difficult.
Before the port, Bun contained 535,496 lines of Zig excluding comments, according to Sumner’s Rust rewrite account. The project also relied on C and C++ components, including JavaScriptCore, SQLite, BoringSSL, and several networking libraries.
A traditional rewrite would have created two moving targets. Engineers would need to reproduce existing behavior while the production Zig version continued receiving fixes and features.
Sumner estimated that a small team would have needed about a year. Such a schedule would either delay product work or force developers to maintain two implementations simultaneously.
Instead, Sumner chose a structure-preserving port. Claude would translate the existing design into Rust with minimal behavioral changes. Architectural cleanup could follow after compatibility was established.
That distinction is important. Claude Code did not invent a new runtime from a product specification. It used the Zig implementation as an executable reference and Bun’s TypeScript test suite as an external judge.
Sumner first spent about three hours working with Claude on a porting guide. The resulting document mapped Zig types, ownership patterns, and common idioms to Rust equivalents.
A separate workflow examined struct fields and proposed Rust lifetimes. A lifetime describes how long a reference remains valid, allowing Rust’s compiler to reject many unsafe memory relationships.
Those decisions became shared artifacts rather than private agent reasoning. Every translation worker could consult the same rules, while reviewers could flag departures from them.
Sumner then tested the approach on three files. One agent translated each file, two isolated agents reviewed it, and another agent applied accepted fixes.
The pilot exposed translation patterns before they spread across 1,448 Zig files. Sumner discarded the trial output because the objective was improving the process, not preserving early code.
Once the rules stabilized, four workflow shards each ran 16 Claude instances. At peak output, the system reportedly generated about 1,300 lines per minute.
The raw translation still did not work. That admission is central to understanding the project.
Claude’s first job was to create a complete candidate implementation. Compilers, tests, and adversarial reviewers then converted that candidate into functioning software.
The process produced 6,502 commits. At one point, the Rust compiler reported approximately 16,000 errors. Anthropic treated those failures as a queue rather than as evidence that the experiment had failed.
Fixing compilation exposed deeper incompatibilities between the languages. Zig’s lazy compilation had tolerated some cyclic imports that Rust’s module system rejected.
Agents categorized those errors and changed the migration rules. They then regenerated or repaired the affected units systematically.
According to Anthropic’s migration account, Bun’s complete existing test suite passed in continuous integration before the code merged. The Rust port shipped inside Claude Code in June 2026.
Nineteen regressions surfaced after the merge. Anthropic says all 19 have since been fixed.
Those regressions prevent a simplistic victory narrative. Passing a large test suite did not establish perfect behavioral parity. It established enough confidence to ship, monitor production, and repair missed cases.
Why Bun Was Willing to Leave Zig
Claude Code reduced the migration barrier, but recurring memory defects supplied the business reason to cross it.
Sumner has been careful not to blame Zig for Bun’s stability problems. Zig helped him build the first version of Bun in one year, before coding models became widely available.
The difficulty came from Bun’s particular workload. Bun connects JavaScript’s garbage-collected objects with manually managed native memory and several C or C++ libraries.
That boundary creates difficult ownership questions. Engineers must know who frees each allocation, whether callbacks outlive native handles, and whether garbage-collected references remain visible.
Bun already used AddressSanitizer, safety-checked builds, continuous fuzzing, and memory leak tests. Yet its release notes continued to include use-after-free errors, double frees, leaks, and race conditions.
A use-after-free occurs when software accesses memory after releasing it. A double free releases the same allocation twice, potentially corrupting the process.
These failures often live in unusual timing paths. Tests must reproduce the right callback order, exception path, or state change before the bug becomes visible.
Rust moves part of that burden into its type system. Its ownership rules determine which value controls memory, while the borrow checker rejects conflicting or invalid references during compilation.
Rust’s Drop mechanism also runs cleanup when a value leaves scope. This reduces reliance on remembering a cleanup statement at every relevant call site.
The language does not remove all memory risk. Bun still interfaces with native libraries and JavaScriptCore, so some operations cannot fit entirely inside safe Rust.
Anthropic reports that approximately 4% of the ported Rust code uses unsafe blocks. Unsafe Rust permits operations that the compiler cannot fully verify, placing their correctness back on developers and reviewers.
Still, Sumner’s case was not that Rust makes Bun incapable of crashing. It was that Rust moves more defects into an earlier feedback loop.
A compiler error arrives before code runs. AddressSanitizer and fuzzing require execution, while production telemetry arrives after users encounter a problem.
That ordering changes the economics of prevention. A rejected compilation is usually cheaper than diagnosing a rare runtime failure across platforms.
Bun says version 1.4 fixed 128 defects reproducible in version 1.3.14. These included memory leaks, crashes, and smaller compatibility issues.
The team also reported lower memory consumption in a repeated-build benchmark. Running the same build 2,000 times used 6,745 MB before the port and 609 MB afterward.
Anthropic says the new binary is 19% smaller on Linux and Windows. It also reports performance improvements between 2% and 5% across HTTP serving and selected real-world workloads.
These figures come from Anthropic and Bun, not an independent benchmark. They should be read as project results that outside users can now test.
The performance gains also were not the original promise. The primary goal was to reduce recurring stability risks without suspending Bun’s roadmap.
That makes the port more consequential than an AI speed demonstration. It connected a measurable engineering liability with a target language that catches more ownership errors mechanically.
Claude Code supplied the labor capacity. Rust supplied the stricter judge.
The Breakthrough Was the Verification Loop, Not Code Generation
The project worked because every failure became structured input for another controlled pass through the system.
Large language models can produce convincing code that compiles and remains wrong. Bun’s migration contained direct examples of that problem.
One translated function passed a native pointer into an asynchronous close operation. Rust then dropped the owning box too early, leaving the native library with freed memory.
Another translation represented timestamps before 1970 incorrectly. A third eagerly evaluated a fallback expression and could panic while processing a valid CSS color function.
All three examples reportedly compiled. Their surface plausibility made ordinary visual review unreliable, especially inside a million-line change.
Sumner separated authorship from review. Each implementation agent received the original Zig file, porting rules, and its own working context.
Reviewer agents received the resulting difference in separate contexts. They were instructed to assume the code was wrong and search for a specific failure.
The separation attempted to prevent agents from defending their previous reasoning. Two reviewers examined each unit, with another agent handling disputes or fixes.
Anthropic calls this adversarial review. It is not proof of correctness, since reviewers can share blind spots or misunderstand the same behavior.
Its value comes from role separation and repetition. A reviewer has one measurable objective, while the implementation agent has another.
The workflows also avoided using the compiler indiscriminately. Running a complete Rust build inside every parallel task would have created contention and wasted compute.
Translation agents instead wrote files according to the rulebook. A centralized compilation stage generated an error list after the fan-out completed.
Fixer agents divided that list by crate and module. They committed targeted corrections, while orchestration scripts controlled when another complete build ran.
Tests played the same role after compilation. Every failed assertion became a concrete queue item tied to old and new behavior.
When a failure appeared once, an agent could fix the affected implementation. When the same pattern appeared repeatedly, the team changed the upstream translation rule.
That approach follows Anthropic’s key doctrine: fix the process that produced the code, not only the generated code itself.
A one-off patch improves one file. A rule change can correct every file created under the same mistaken assumption.
Anthropic has published a generalized migration kit containing prompts, templates, queue scripts, and safety settings. The kit begins with a feasibility assessment, where declining a migration remains an accepted result.
Its six stages cover mapping, rule creation, stress testing, translation, compilation, execution, and behavioral comparison. Each gate demands a mechanical exit condition.
The approach resembles a manufacturing line more than conversational programming. Agents specialize, shared artifacts carry policy, and automated checks reject defective output.
This pattern also explains why Anthropic considers migration especially suitable for AI agents.
First, the source code already describes much of the desired behavior. Agents do not need to infer an entirely new product from incomplete requirements.
Second, many files can be translated independently once dependencies are mapped. That permits parallel work without forcing every agent to understand the complete repository.
Third, compilers and tests provide objective feedback. Agents can repeat a loop without asking a human to judge every local decision.
Fourth, failures create their own backlog. A compiler error, crash, or output difference specifies the next task.
Anthropic’s dynamic workflows extend this model beyond static subagent lists. Claude can write orchestration scripts, launch parallel workers, inspect their results, and revise the workflow.
That autonomy raises the throughput ceiling. It also increases the importance of permissions, isolation, and resumable state.
Sumner encountered the danger early. Parallel agents began running conflicting Git operations, including commands that could overwrite one another’s work.
He revised the workflow to prohibit broad Git commands. Agents could commit specific files, while the orchestrator maintained the larger migration state.
This incident is more instructive than a polished demo. Agent failures do not always appear as incorrect code. They can damage coordination, repository state, or the evidence needed to audit a run.
The engineering advantage therefore comes from constrained autonomy. The system performs more work because its operating boundaries are explicit.
Passing Every Test Still Left 19 Regressions
Bun’s post-merge bugs show that test completion is a release threshold, not a mathematical guarantee of equivalence.
Anthropic’s headline result sounds definitive: 100% of Bun’s existing test suite passed before the merge. The 19 later regressions reveal what that percentage cannot measure.
A test suite covers the behaviors its authors anticipated and encoded. It does not automatically represent every environment, timing sequence, integration, or user workload.
Language migrations also change hidden properties. Allocation timing, destructor order, thread scheduling, and foreign-function boundaries can differ even when visible outputs match.
Bun benefited from an unusually helpful design choice. Its main test suite was written in TypeScript rather than Zig, so it could exercise either runtime through the same external interface.
Many legacy systems lack that independence. Their tests call private functions, inspect internal data structures, or rely on language-specific mocking behavior.
Moving such tests into the new language risks reproducing the implementation instead of preserving the original contract. A mistaken port can then agree with mistaken tests.
Anthropic recommends dividing tests into portable and implementation-dependent groups. Teams should run portable tests against both versions and verify that intentionally broken builds fail.
That last step matters. A test that always passes is worse than no test because it creates false confidence.
For an internal Python-to-TypeScript migration, Anthropic Labs co-lead Mike Krieger reportedly lacked a complete portable suite. His team built a parity harness around seven real-world scenarios.
The harness ran commands against the Python original and the TypeScript replacement, then compared their outputs. Any behavioral difference became a bug.
Krieger’s migration produced 165,000 lines of TypeScript over a weekend. Anthropic says it used hundreds of agents, eight phase gates, and three adversarial review rounds.
The team also discarded two complete attempts. Each run exposed weaknesses in the workflow, allowing the next attempt to improve the rules and verification process.
On the third run, the port passed its parity checks. Claude then generated additional end-to-end tests and reportedly repaired failures over four nights.
That example reinforces the same lesson as Bun. Fast generation becomes useful when abandoning a bad run is affordable.
Traditional rewrites accumulate sunk cost over months. Teams hesitate to restart, even after discovering that an early architectural choice was wrong.
Agentic migrations can regenerate large portions after changing the rules. The branch becomes disposable while the verified process becomes the durable asset.
Still, regeneration is not free. Anthropic warns that dynamic workflows can consume substantially more tokens than ordinary Claude Code sessions.
Teams also need experienced engineers to design acceptance criteria, monitor workflows, interpret systemic failures, and decide whether behavioral changes are acceptable.
Bun’s 4% unsafe code deserves continued scrutiny. Unsafe blocks are sometimes necessary at native boundaries, but they are exactly where Rust’s strongest compiler guarantees stop.
A mechanical port can also preserve historical complexity. Sumner intentionally chose a line-oriented translation because redesigning the architecture would have increased risk.
That choice accelerated parity, but it did not automatically produce idiomatic Rust. Bun plans to reduce unsafe usage and refactor the implementation after shipping version 1.4.
The source and target versions can also diverge during a long validation cycle. A two-week migration limits this problem, but busy repositories can change substantially even within that window.
Security review presents another challenge. Tests establish observed behavior, while security analysis must consider malicious inputs and unexpected state combinations.
The published evidence therefore supports a narrower conclusion than “Claude can rewrite any codebase.” It supports migrations with strong specifications, parallelizable work, and objective behavioral checks.
Projects with undocumented requirements, weak tests, or hardware-dependent timing have a harder path. Their missing specification becomes the primary project, regardless of agent speed.
Teams considering a similar effort should first improve the judge. A searchable collection of technical decisions can help preserve the context behind those rules.
For example, an engineering knowledge base can connect design notes, migration policies, and test evidence. That record becomes especially useful when hundreds of agents produce thousands of commits.
The central risk is no longer merely that AI writes bad code. It is that organizations mistake high output and green tests for complete understanding.
What Engineering Teams Should Watch Next
The next stage will be judged by sustained production quality, lower unsafe exposure, and successful migrations outside Anthropic’s unusually favorable environment.
The first signal is Bun’s production record over the next several releases. The important numbers are not generated lines or peak commits per hour.
Developers should watch regression counts, crash reports, memory defects, and compatibility failures after more users adopt the Rust build.
If those measures remain below the Zig baseline, Anthropic’s case becomes stronger. If new failures cluster around translated code, the pre-merge verification process needs another revision.
The second signal is the share and location of unsafe Rust. A reduction from the reported 4% would show that the mechanical port can mature into a more idiomatic implementation.
Location matters as much as percentage. Small unsafe boundaries around audited C interfaces present a different risk from unsafe logic distributed across core runtime components.
Bun’s public repository gives outside Rust developers a chance to inspect those decisions. Independent benchmarks and bug reports will provide evidence beyond Anthropic’s own measurements.
The third signal is whether other organizations reproduce the method. Bun had an independent TypeScript test suite, a highly technical founder, and direct access to Anthropic’s newest model.
A convincing broader result would involve a legacy system with weaker documentation, internal tests, multiple owners, and regulatory or security requirements.
Anthropic’s second case, the Python-to-TypeScript port, moves in that direction. However, it remains an internal project described by the company that built the tool.
The company says developers migrated ten packages during the month before its July 16 announcement. Those projects ranged from tens to hundreds of thousands of lines.
More detailed case studies should reveal how often teams abandon attempts, how many human hours they spend, and which defects survive automated review.
Competing coding agents will also face pressure to support long-running, resumable workflows. Single-file completion matters less when the valuable task spans thousands of interdependent units.
The product contest will increasingly center on orchestration, permissions, evaluation, and recovery. Model intelligence remains necessary, but it does not supply operational discipline by itself.
Engineering leaders should resist treating migration as an automatic modernization strategy. Anthropic’s own starter process begins by asking whether the rewrite should happen at all.
A successful port preserves behavior. It does not guarantee a better architecture, clearer product requirements, or reduced operational complexity.
The strongest candidates have a concrete technical liability and a target platform that addresses it. They also have external tests that can judge both implementations equally.
Bun met those conditions. Manual memory management contributed to recurring stability work, while Rust moved more ownership checks into compilation.
Claude Code then made the once-impractical transition fast enough to attempt without suspending the roadmap. That combination, not AI output alone, created the result.
The Anthropic Claude Code migration changes the question engineering teams can reasonably ask. A complete language port no longer needs to begin as a multi-year commitment.
It can begin as a bounded experiment with explicit gates and a disposable branch. Failure can produce better rules instead of an abandoned rewrite.
But the burden of proof has not disappeared. It has moved from “Can agents generate the code?” to “Can the organization build a judge that reliably rejects the wrong code?”
That is the question teams should test before launching hundreds of agents. Choose one constrained component, define observable parity, and deliberately break the judge.
If the system catches those failures, a larger migration becomes credible. If it does not, faster code generation will only produce uncertainty at greater scale.


