Friday, July 10, 2026
Bun Drops Zig for Rust — 1M+ Lines in 11 Days with Claude Fable 5. This Changes Migration Economics.
Posted by

On July 8, Jarred Sumner published a post on the Bun blog with a title that sounds like science fiction: "Rewriting Bun in Rust." The subhead is where it gets real.
1,448 Zig files. 11 days. 64 Claudes. 1 million lines of Rust.
Bun — the JavaScript runtime, bundler, transpiler, package manager, test runner, and HTTP server that processes 22 million monthly CLI downloads — has been fully rewritten from Zig to Rust. The rewrite was executed not by a team, but by a single engineer using a pre-release version of Anthropic's Claude Fable 5 running inside Claude Code's dynamic workflows across 64 parallel instances.
The API bill: roughly $165,000 (5.9 billion uncached input tokens, 690 million output tokens).
A human team of three engineers with full codebase context would have needed about a year, according to Sumner.
This is the most concrete, measurable, large-scale AI-driven code migration we have ever seen. And it changes the economics of language migration projects permanently.
Why Leave Zig at All?
Let me be clear: this isn't a story about Zig being bad. Sumner spends the first half of his post praising Zig for making Bun possible at all — it let one person build a runtime of this scope in a single year. The tone isn't "Zig failed us"; it's "Zig was perfect for building Bun, but Rust is better for keeping it stable."
The problem was memory safety at the boundary between Zig and a garbage-collected runtime.
Bun sits on JavaScriptCore, which has a garbage collector. Zig has manual memory management with no destructors, no RAII, no compiler-enforced ownership model. Cleanup relies on defer at every call site — and humans forget defer calls in error paths, edge cases, and re-entrant callback scenarios.
The bug list from Bun v1.3.14 reads like a catalog of every kind of memory corruption:
heap-use-after-freeinnode:zlib(async.reset()during.write())- Use-after-free in
node:http2(re-entrant JS callbacks triggering hashmap rehash) UDPSocket.send()use-after-free (valueOf callbacks detaching buffers)Buffer#copyout-of-bounds reads (detached ArrayBuffers during coercion)- Double-free in the CSS parser (
background-clipwith vendor prefixes) - Memory leak in
tlsSocket.setSession()(~6.5KB per call)
Sumner puts it bluntly: "Our bugfix list felt bad and I was tired of going to sleep worrying about crashes in Bun."
Why not C++? Destructors are implicit, but still reliant on style guides. Memory corruption would persist.
Why not stay in Zig? Homegrown smart pointers had "worse ergonomics than Rust, with none of the guarantees."
Why Rust? Drop is implicit, enforced by the compiler. In safe Rust, use-after-free and double-free are compile-time errors, not runtime surprises. "Compiler errors are a better feedback loop than a style guide," Sumner writes.
The bet was: what if I spend a week testing if Fable 5 can rewrite Bun in Rust?
The Workflow: How You Run 64 Claudes in Parallel for 11 Days
This isn't "I asked Claude to port my codebase and it did." The methodology Sumner developed is probably the most important artifact from this whole story — it's a blueprint for anyone attempting massive AI-assisted code migration.
Phase 1: Planning (3 hours)
Instead of jumping into code, Sumner had Claude generate two planning documents:
PORTING.md: A mapping of Zig idioms to Rust equivalents. How does Zig's error union pattern translate? What about Zig'scomptime? This was the migration playbook.LIFETIMES.tsv: A per-struct-field lifetime analysis. Which pointers are owned? Borrowed? Which are subject to GC reachability? This prevented the most common Rust footgun from derailing the port.
Both documents were adversarially reviewed by separate Claude instances before any code was written.
Phase 2: Mechanical Port (1,448 files)
The straightforward part: translate every .zig file to a .rs file, following the porting playbook. This is the part that's closest to "Claude as fancy sed" — but it required understanding each file's semantics, not just syntax.
Phase 3: Crate Decomposition
Zig doesn't have a module system like Rust's crates. The dependency graph had cycles that needed to be broken. Claude classified the dependency structure and refactored the codebase into roughly 100 crates with clean dependency boundaries.
Phase 4: Fix 16,000 Compiler Errors
This is where the parallel scaling shines. Sumner's workflow dumped ~16,000 errors grouped by crate, then divvied them up among 64 Claude instances across 4 separate worktrees. The topology: 16 loops × 4 worktrees, where within each loop, one Claude fixes errors, two Claudes review the fix, and one Claude applies it.
This is the adversarial review pattern, and it's the most important design insight in the whole post:
The reviewer's only job: find bugs & reasons why the code does not work. The implementer doesn't review. The reviewer doesn't implement.
Bugs caught by adversarial review before merge:
- Async close use-after-free: A raw pointer passed to libuv was dropped at the end of a match arm (Box dropped immediately). Fix:
Box::leak. - Negative time panic:
trunc()on a negativef64produced a negativensecvalue, which is illegal fortimespec. Fix:floor()to keepnsecin[0, 1e9). - Eager
unwrap_orpanic: Rust's eager argument evaluation caused a panic beforeunwrap_orcould use its default value. Fix:unwrap_or_else.
All three compiled. All three looked plausible. Only the adversarial review caught them.
Phase 5-6: Smoke Tests → Full Test Suite
Get bun --version and bun test running. Then loop over failing test files until CI goes green.
The Numbers
| Metric | Value |
|---|---|
| Duration | 11 days |
| Total commits | 6,502 (peak 695/hour, 58/min) |
| Peak LLM instances | ~64 Claudes simultaneously |
| Lines of Rust written | +1,009,272 |
| Files ported | 1,448 Zig → Rust |
| API cost | ~$165,000 |
| Input tokens | 5.9 billion |
| Output tokens | 690 million |
| Test pass rate (Linux x64 glibc) | 99.8% |
| Tests skipped | 0 |
| Regressions introduced | 19 (all fixed) |
| Performance improvement | 2-5% faster |
| Bugs fixed | 128 |
Let me contextualize the financials: $165,000 is a lot of money for API calls. But compared to three senior engineers for a year — call it 3 × $250K loaded cost = $750K — it's less than a quarter of the cost, and the work was done in 11 days instead of 12 months. The ROI on velocity alone is staggering.
The Regressions: Where Mechanical Porting Breaks
Sumner documents 19 regressions that made it through the pipeline. Every single one is instructive because they all share a pattern: code that looks identical but behaves differently.
The best example: debug_assert! vs Zig's assert. In Rust, debug_assert! is a macro that is erased entirely in release builds. In Zig, the standard assert always runs. A side-effect-bearing assertion that passed in debug silently disappeared in release, causing a subtle behavioral change. These are the bugs that unit tests don't catch — they require understanding the semantic guarantees of each language's standard library, not just the logical correctness of the ported code.
Not the First, But Definitely the Most Concrete
This isn't the first large AI-assisted code migration. We've seen companies use Claude Code and similar tools to port internal codebases between languages. But this is the first one where we have:
- Full public accounting — costs, token counts, error rates, regressions
- The code — the PR is public with +1M lines
- The methodology — detailed workflow description from the engineer who did it
- The before/after — Bun v1.4.0 canary is running the Rust code now
There's one obvious caveat: Sumner is an Anthropic employee (Bun was acquired by Anthropic in December 2025), and he used a pre-release version of Fable 5. This is the tool's creator, rewriting his own codebase, with unreleased model access, at what is effectively zero marginal labor cost. Replicating this exact outcome as a third party is not straightforward.
But the methodology — planning documents, adversarial review, parallel fix loops, crate decomposition — is fully transferable. And it's not hard to imagine a world where within 12-18 months, a standard migration toolkit exists that wraps this workflow into a push-button service.
What This Means
Three takeaways:
1. Language migration projects just got 10-30x cheaper and faster. If your company has a legacy codebase in a memory-unsafe language that you've been putting off migrating, this case study is the spreadsheet cell that changes the conversation. The ROI on any migration where you can define a clear porting playbook (Java → Kotlin, Python → Rust, C → Rust, etc.) just shifted dramatically.
2. The adversarial review pattern is the killer methodology. The insight that split context windows — one agent implementing, two agents reviewing — catches bugs that compiled, looked correct, and would have shipped, is a workflow design pattern that generalizes far beyond code migration. Every AI-assisted code pipeline should have adversarial review baked in.
3. We're past the "can AI write a million lines?" question. The answer is yes, it can — in 11 days. The question is now: can it maintain, debug, and evolve that codebase? The Rust port of Bun is identical architecture, same data structures, same behavior. The interesting follow-up will be whether the Rust version actually accumulates fewer crashes over time — which is the whole point of the switch. If the hypothesis holds, this was a one-time migration cost for permanent reduction in stability debt.
Simon Willison's writeup of the story captures the mood best: this is simultaneously the most impressive and most terrifying AI coding story of the year. Impressive because it worked. Terrifying because of course it worked — we're already at the point where a single person with the right model can do what used to take a team a year.
Bun v1.4.0 is available as a canary release. It runs entirely on Rust now. The fact that I can type that sentence without it being a joke is the story.