Bun's Rust Rewrite: 11 Days, 6,778 Commits, and What Actually Changed
Bun’s team shipped something unusual in May 2026: they replaced the entire native core of the runtime with a Rust port, and they did it in 11 days.
The disclosure at the top of the announcement sets the tone. Bun was acquired by Anthropic in December 2025. Jarred Sumner, Bun’s creator, used a pre-release version of Claude — a “Mythos-class model” they call Fable 5 — for much of the work. This isn’t a thought experiment. Bun v1.4.0, the first Rust version, is in canary now.
The raw numbers:
- 535,496 lines of Zig (excluding comments) became a Rust codebase
- 6,778 commits between May 3 and May 14
- 1,448
.zigfiles mechanically ported to.rs - ~$165,000 in API spend (5.9B uncached input tokens, 690M output tokens)
- Peak: 64 Claudes running at once across 4 worktrees

Bun was always a Zig bet
Bun started as a line-for-line port of esbuild’s JS/TS transpiler from Go to Zig. Sumner wrote his first line of Zig on April 16, 2021, after seeing the single-page Zig language reference on Hacker News. The initial version — transpiler, minifier, bundler, npm-compatible package manager, Jest-like test runner, Node.js API surface — was written by one person in one year, in a cramped Oakland apartment, before LLMs were useful for this kind of work.
That bet paid off. Bun’s CLI now sees over 22 million monthly downloads. Claude Code and OpenCode use it as their runtime. Vercel, Railway, and DigitalOcean ship first-party support.
The scope was also the problem.
The debt: GC memory next to manual memory
JavaScript is garbage-collected. Bun embeds JavaScriptCore (the engine in Safari) plus a pile of C/C++ libraries: uWebSockets, BoringSSL, SQLite, lsquic. Zig, like C, does not manage memory for you. Bun’s job is to sit between a GC’d language and manually-managed native code, and that boundary is where most of its worst bugs lived.
The announcement lists a sample of what they fixed in v1.3.14 alone: a heap-use-after-free in node:zlib, use-after-free in node:http2 from re-entrant callbacks, out-of-bounds writes in UDPSocket.sendMany, a memory leak per tls.connect, a double-free in the CSS parser. The list goes on, and it’s technical.
Sumner is explicit that this is not Zig’s fault. “We wouldn’t have gotten this far if not for Zig, and I’ll always be grateful.” The issue is structural: handling lifetimes across a GC boundary is hard in any language that doesn’t design for it, and Zig’s answer — explicit defer at every call site — is easy to get wrong in rarely-reached error paths.
He weighed C++. About 20% of Bun is already C++, and moving more would give constructors and destructors. But it would still lean on style guides enforced through code review, and memory corruption would still happen.
Rust’s pitch is different: in safe Rust, use-after-free, double-free, and “forgot to free” are compiler errors. The borrow checker and Drop turn a style-guide problem into a type-system problem.
The strategy: transpile, don’t redesign
Rewrites have a bad reputation, and for good reason. A from-scratch rewrite of 535K lines would freeze features and fixes for a year. Bun’s chosen shape was a mechanical port: same architecture, same performance targets, same features, same test suite — just in Rust.
Two decisions drove the whole effort:
- All at once, not incremental. Sumner’s experience porting esbuild to Zig told him incremental rewrites leave temporary scaffolding that hurts more than it helps.
- Make it look transpiled. The goal was Rust that reads like the Zig it came from, to be refactored toward idiomatic Rust after v1.4 ships.
Crucially, Bun’s test suite is written in TypeScript, so it doesn’t care what language the runtime is implemented in. That let them treat “all tests green” as the definition of done.
The prep was small but deliberate. Before writing code, Sumner spent ~3 hours with Claude mapping Zig patterns to Rust; that conversation became PORTING.md (it later hit Hacker News). A second pass analyzed the proper Rust lifetimes of every struct field across the codebase and serialized them into LIFETIMES.tsv. Both documents went through adversarial review before a single line was ported.
What stayed put: JavaScriptCore and the embedded C/C++ libraries. The rewrite was the native glue and the Bun-specific code, not the JS engine.
Execution: 64 Claudes, 11 days
The port ran as about 50 “dynamic workflows” in Claude Code, continuously, for 11 days. Each workflow was a loop: take a task, produce code, get it reviewed, apply feedback.
The review model is the interesting part. For every implementer, there were two or more adversarial reviewers — separate Claude sessions told to assume the code is wrong and to find every reason it fails. The implementer never reviews; the reviewer never implements. The announcement frames this as mirroring human review: the author wants to merge, so a different party checks.
A few mechanics are worth pulling out:
- Trial run first. They ported 3 files before committing to all 1,448.
- Worktree sharding. Early runs had Claudes stomping on each other with
git stashandgit reset. The fix: 4 worktrees, 16 Claudes each, with a rule to never run git or cargo mid-task. - Compiler errors as a work queue.
cargo checkdumped ~16,000 errors to a file, grouped by crate, and 64 Claudes chipped away — 16 fix/review/apply loops across 4 worktrees. - Cyclical dependencies. Zig was effectively one compilation unit; Rust needed ~100 crates. Untangling the cycles surfaced most of those 16,000 errors.
- Isolation. Stress tests that exhaust TCP sockets or spawn ~10k processes ran under
systemd-runcgroups. The machine still ran out of disk and crashed a few times.
timeline
title Bun's 11-day Zig → Rust rewrite (May 2026)
2026-05-03 : Port branch opened (PR #30412)
2026-05-04 : First 100-file draft batch
2026-05-06 : ~16,000 compiler errors, 64 Claudes
2026-05-08 : First CI run (972 failing files)
2026-05-09 : Linux x64 turns green
2026-05-11 : Windows green (last platform)
2026-05-14 : All 6 platforms green, merged
By the numbers: peak 1,300 lines of code per minute, 695 commits in the busiest hour (May 6), 58 commits in the single busiest minute. Every commit was reviewed by two adversarial reviewers before landing. The final diff was +1,009,272 lines. Zero tests were skipped or deleted.
CI is where it became real. Two days after the first run, failing test files dropped from 972 to 23. A day and a half later Linux went fully green. Windows finished last on May 11; all six platforms were green on build #54202, May 14.
What Rust actually bought
The stated goal was stability, and the post backs that with hard numbers.
Bugs. Bun v1.4.0 fixes 128 bugs that reproduce in v1.3.14.
Memory. Drop replaced per-call-site defer. The post’s example: bundling the same 60-module project 2,000 times in one process. In v1.3.14 every build leaks ~3 MB forever; in v1.4.0 memory levels off.
| Builds | v1.3.14 | v1.4.0 |
|---|---|---|
| 500 | 1,914 MB | 526 MB |
| 1,000 | 3,506 MB | 586 MB |
| 1,500 | 5,097 MB | 608 MB |
| 2,000 | 6,745 MB | 609 MB |
A previous attempt to fix this in Zig was never merged, Sumner writes, because the lack of a Drop equivalent made it hard to feel confident.
Binary size. The Rust rewrite alone cut 3.8 MB (Windows), 5.5 MB (macOS), 6.8 MB (Linux) — mostly from dropping excessive Zig comptime. Further linker work (identical code folding, trimming ICU data) brought total shrinkage to ~20% on Linux and Windows.
| Version | Platform | Size |
|---|---|---|
| v1.4.0 | Windows | 76 MB |
| v1.3.14 | Windows | 94 MB |
| v1.4.0 | Linux | 70 MB |
| v1.3.14 | Linux | 88 MB |
Speed. Cross-language LTO between C/C++ and Rust lets the compiler inline across language boundaries. Bun measured 2–5% gains:
Bun.serve: 169.6k → 177.7k req/s (+4.8%)express: 64.5k → 66.6k (+3.2%)next build: 13.62s → 13.03s (+4.5%)tsc -b --force: 0.94s → 0.89s (+4.7%)
Prisma moved its Compute public beta onto the Rust rewrite. Alexey Orlenko: “We ran into memory leaks and a connection pool that couldn’t recover after a VM was paused and resumed. When the Rust rewrite appeared, we tested it against the same failure modes. It handled them perfectly.”
Claude Code v2.1.181 (June 17) and later use the Rust port. Startup is 10% faster on Linux. Sumner’s verdict on the user-visible change: “Boring is good.”
Where the port slipped
A faithful transpile of 535K lines is not free. The post documents 19 known regressions, all fixed. The instructive ones are tiny semantic gaps between languages:
debug_assert!side effects. Zig’sassertis a function, so its argument runs in every build. Rust’sdebug_assert!is a macro erased in release — aninsert_stalecall that mutated HMR state silently stopped running. (#30678)- Odd-length slices. Zig’s helper ignored a trailing odd byte;
bytemuck::cast_slicepanics on it.Blob.text()on a UTF-16 BOM plus an odd byte count started crashing. (#31188) - Bounds checks. Zig’s
ReleaseFaststrips them; Rust keeps them. A placeholder constant dropped the filename-interning ceiling from 8.4M to 270K, and real projects hit it. (#31503) comptimeformat strings. Zig evaluates format strings at compile time, so color markers are gone before arguments are substituted. Rust has nocomptime, so the marker parser ate a literal backslash in OSC 8 hyperlinks. (#30693)
These are the bugs that only surface because two languages look identical but aren’t.
What this means
Strip away the LLM angle and there’s a real engineering story: a project hit the ceiling of what manual memory management could give it at scale, and chose Rust’s borrow checker as the tool to make a whole class of bugs impossible rather than merely discouraged.
The LLM angle is the part people will argue about. Sumner is candid that this would have taken three engineers with full codebase context about a year, and that they’d never have done it — the realistic alternative was “do nothing and keep fixing the bugs.” Instead, one engineer supervising 64 Claudes did it in 11 days for ~$165K.
About 4% of Bun’s Rust code is in unsafe blocks (~13,000 unsafe keywords across ~27,000 lines of ~780,000 total), and 78% of those blocks are a single line. Since the merge, they’ve run 11 rounds of security review and added 24/7 coverage-guided fuzzing across every parser — 100 billion executions so far, ~15 PRs.
The maintainability question is the one I’d watch. Sumner says the Rust reads like the Zig, and shows a side-by-side of canMergeSymbols to make the point. The bet is that a faithful port is reviewable port-by-port, which is what let one person sign off on a million-line diff.
His closing line is the one that will age interestingly: “One engineer can do a lot more today than a year ago.”