How Bun Rewrote 1 Million Lines from Zig to Rust in 11 Days Using AI

On May 14, 2026, Jarred Sumner merged PR #30412 into Bun's main branch — a pull request titled simply "Rewrite Bun in Rust." The diff added over one million lines of Rust code, fixed 128 reproducible bugs, shrank the binary by 20%, and made the runtime 2–5% faster. The entire rewrite took 11 days, consumed 5.9 billion uncached input tokens, and cost approximately $165,000 in API fees. Not a single line was written by a human.

This is the story of how Bun — the JavaScript runtime that now sees 22 million monthly CLI downloads — moved from Zig to Rust, why it happened, and what it means for the JavaScript ecosystem.

Why Zig Wasn't Enough

Bun started in April 2021 as a line-for-line port of esbuild's TypeScript transpiler from Go to Zig. Jarred Sumner wrote the first version in one year, in a cramped Oakland apartment, before LLMs existed. Zig made Bun possible — its low-level control and performance characteristics allowed a single developer to build a transpiler, bundler, package manager, test runner, and Node.js compatibility layer in record time.

But Bun's scope created a unique challenge. The runtime mixes garbage-collected values from JavaScriptCore with manually managed native memory. Zig, like C, requires explicit cleanup at each call site using defer — it has no destructors, no implicit Drop. This led to a pattern of bugs that plagued Bun v1.3.14:

  • Use-after-free in node:zlib when calling .reset() during an in-progress async .write()
  • Double-free in the CSS parser with vendor-prefixed background-clip and multi-layer backgrounds
  • Memory leak in tlsSocket.setSession() where each call leaked one SSL_SESSION (~6.5 KB per call)
  • Leaked DuplexUpgradeContext on every tls.connect({ socket: duplex }) call
  • Race condition in MessageEvent where the GC marker thread could observe torn data during concurrent access

These bugs weren't caused by Zig itself — other Zig projects like TigerBeetle maintain remarkably few memory issues. The problem was the combination of Bun's architecture (constant GC boundary crossing) with Zig's manual memory management model. No language was really designed for this specific use case.

The Rewrite Decision

On a practical level, fixing these bugs one at a time was unsustainable. Bun had already tried a Rust-inspired smart pointer approach in Zig, but it offered worse ergonomics than Rust with none of the guarantees. The team considered:

  1. Writing a rigid style guide — But enforcement through code review doesn't scale
  2. Switching to C++ — Would get constructors and destructors, but still relies on style guides for memory safety
  3. Rewriting in Rust — Would turn entire classes of memory bugs into compiler errors

The blocker for option 3 had always been time. A human rewrite of Bun's 535,496 lines of Zig would take a small team about a year — during which no bugfixes, security patches, or feature development could ship.

The question became: what if an AI could do the mechanical translation while humans focused on review?

How the Rewrite Worked

Sumner used a pre-release version of Anthropic's Claude Fable 5, a Mythos-class model, through Claude Code's dynamic workflows. The process wasn't a single prompt — it was approximately 50 carefully designed workflows running continuously over 11 days.

Phase 1: Preparation (3 hours)

Before writing any Rust, Sumner spent three hours discussing the Zig-to-Rust mapping with Claude. This produced two critical documents:

  • PORTING.md — A comprehensive guide mapping Zig patterns and types to Rust equivalents
  • LIFETIMES.tsv — A file tracing every struct field's lifetime to determine proper Rust ownership

Both documents went through adversarial review — separate Claude instances whose only job was to find errors and inconsistencies.

Phase 2: Trial Port (3 files)

Before porting all 1,448 Zig files, Sumner started with just 3. Each file went through:

  1. One implementer Claude translated the Zig to Rust
  2. Two adversarial reviewers checked behavioral equivalence against the porting guide
  3. One fixer applied the feedback

This validated the approach and caught systematic issues early.

Phase 3: Parallel Translation

The actual translation ran 4 workflow shards simultaneously, each in a separate worktree, with 16 Claude instances per workflow. At peak, 64 Claude instances ran in parallel, writing approximately 1,300 lines of Rust per minute.

Every line was reviewed by two adversarial reviewers before committing. The rule was explicit: if you need a paragraph-long comment to justify why a workaround is OK, the code is wrong — fix the code.

Phase 4: Compiler Errors as a Work Queue

After translation, the codebase had approximately 16,000 compiler errors from cyclical dependency resolution alone. The workflow became:

  1. For each crate, run cargo check
  2. Group errors by file
  3. Fix all errors within the crate
  4. Two adversarial reviewers check the fixes
  5. One fixer applies suggestions

To prevent Claude instances from conflicting, cargo check ran only at the start, and no git commands were used until the end.

Phase 5: Getting Tests Green

Bun's test suite is written in TypeScript, making it language-independent. This was the key enabler — the same tests that validated Zig behavior could now validate Rust behavior. The suite includes:

Platformexpect() callsTestsFiles
Debian 13 x641,386,82660,6244,174
macOS 14 arm641,259,95358,8504,175
Windows 2019 x641,007,54457,3374,173

The CI burndown went from 972 failing test files to 23 in two days, then to zero within another day and a half.

The Results

128 Bugs Fixed

Bun v1.4.0 fixes 128 bugs that reproduce in v1.3.14. The most significant improvement is in memory behavior:

// Bundle the same 60-module project 2,000 times in one process
for (let i = 0; i < 2_000; i++) {
  await Bun.build({
    entrypoints: ["./index.js"],
    minify: true,
    sourcemap: "external",
  });
}
BuildsBun v1.3.14Bun v1.4.0
5001,914 MB526 MB
1,0003,506 MB586 MB
1,5005,097 MB608 MB
2,0006,745 MB609 MB

In v1.3.14, every Bun.build() call leaked about 3 MB — tools like dev servers that bundle on every request eventually ran out of memory. In v1.4.0, memory levels off at around 609 MB, thanks to Rust's Drop trait automatically cleaning up resources when they go out of scope.

Smaller Binaries

The Rust rewrite combined with linker optimizations (identical code folding, lazy ICU decompression) shrunk Bun by approximately 20%:

PlatformBun v1.3.14Bun v1.4.0Reduction
Windows94 MB76 MB-19%
Linux88 MB70 MB-20%

Faster Performance

Rust's cross-language link-time optimization (LTO) between C/C++ and Rust enables inlining across language boundaries:

HTTP throughput (requests/sec, average of 3 rounds):

ServerBun v1.3.14Bun v1.4.0Change
Bun.serve169.6k177.7k+4.8%
node:http103.8k108.5k+4.5%
Elysia158.9k163.3k+2.8%
express64.5k66.6k+3.2%
fastify91.5k95.9k+4.8%

Build workloads:

TaskBun v1.3.14Bun v1.4.0Change
next build13.62 s13.03 s+4.5%
vite build (tsc + vite)1.69 s1.65 s+2.2%
tsc -b --force0.94 s0.89 s+4.7%

Production Validation

Prisma launched the Prisma Compute public beta on Bun's Rust rewrite:

"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." — Alexey Orlenko, Prisma

Claude Code v2.1.181 (released June 17, 2026) uses the Rust port, with Linux p50 startup time dropping from 517ms to 464ms — a 10% improvement that barely anyone noticed. As Sumner put it: "Boring is good."

Node.js Comparison

The Bun-to-Rust rewrite story also highlights a broader comparison between Bun and Node.js when it comes to building reliable JavaScript infrastructure.

Memory Safety in Node.js vs Bun

Node.js, written in C++, faces similar memory management challenges. While V8's garbage collector handles JavaScript memory, native addons and Node.js internals still manual-manage memory. The key difference is that Bun now uses Rust's ownership model:

// Bun: Automatic cleanup with Drop trait
// No manual memory management needed
const server = Bun.serve({
  port: 3000,
  fetch(req) {
    return new Response("Hello!");
  },
});

// Bun handles cleanup automatically when server goes out of scope
// Node.js: Relies on C++ internals + GC
// Native addons still need manual memory management
import http from "node:http";

const server = http.createServer((req, res) => {
  res.end("Hello!");
});

server.listen(3000);

// Must manually call server.close() for proper cleanup
// No compile-time guarantees about resource lifecycle

Binary Distribution

Bun ships as a single binary with everything built in. Node.js requires additional packages for equivalent functionality:

# Bun: Everything is built-in
bun install          # Package manager
bun test             # Test runner
bun build --compile  # Single-file executable
bun sql              # PostgreSQL/MySQL client
bun redis            # Redis client

# Node.js: Multiple packages needed
npm install          # npm (built-in)
npm install jest     # Test runner
npm install esbuild  # Bundler
npm install pg       # PostgreSQL client
npm install ioredis  # Redis client

Stability Improvements

The Rust rewrite specifically addressed bug classes that have historically plagued both Bun and Node.js:

// Before (Bun v1.3.14 / Zig): Memory leak per connection
// Each setSession() call leaked ~6.5 KB of SSL_SESSION memory
import tls from "node:tls";

const socket = tls.connect({ host: "example.com", port: 443 });
socket.setSession(cachedSession); // Leaked SSL_SESSION every call

// After (Bun v1.4.0 / Rust): Drop trait handles cleanup
// SSL_SESSION is automatically freed when no longer referenced
import tls from "node:tls";

const socket = tls.connect({ host: "example.com", port: 443 });
socket.setSession(cachedSession); // No leak — Rust's Drop cleans up

The Rust approach turns these runtime crashes into compile-time errors. In Node.js, equivalent bugs exist in V8 and libuv's C++ code — they're just less visible because those layers are more mature.

The Broader Impact

What This Means for AI-Assisted Development

The Bun rewrite is the largest publicly documented AI-assisted code migration to date. The key insight wasn't that AI can write code — it's that a language-independent test suite creates a tight feedback loop that makes massive mechanical translations feasible.

Sumner's workflow pattern — one implementer, two adversarial reviewers, one fixer — is replicable. The critical prerequisite is a comprehensive test suite that exercises behavior through the public API surface, not implementation details.

What This Means for JavaScript Runtimes

Bun v1.4.0 is currently available as a canary release:

bun upgrade --canary

For teams running Bun in production, the practical question is whether the canary build passes their integration tests. The Rust rewrite preserves the external API surface — the JavaScript and TypeScript interfaces developers actually call. But behavioral edge cases in error paths and concurrent scenarios may differ.

What This Means for Zig

Sumner is careful not to blame Zig:

"I don't blame Zig for that — other users of Zig don't have the bugs we had, and mixing GC with manually-managed memory is an uncommon enough thing for software to need that no language really designs for it."

The rewrite was a maturation decision specific to Bun's architecture, not a verdict on Zig as a language. TigerBeetle's database, built in Zig, maintains famously few memory issues through rigorous style enforcement and a culture that prizes correctness over shipping speed.

What's Next

Bun v1.4.0 will be the first version of Bun written in Rust. Since merging, the team has:

  • Completed 11 rounds of security review from Claude Code Security
  • Added 24/7 coverage-guided fuzzing of every parser (JavaScript, TypeScript, JSX, CSS, JSON5, JSONC, TOML, YAML, Markdown, INI, Bun Shell scripts, semver ranges, .patch files, and CSS colors)
  • The fuzzer has executed parser code approximately 100 billion times, producing about 15 pull requests fixing discovered bugs

About 4% of Bun's Rust code currently sits inside unsafe blocks (~13,000 keywords across ~27,000 lines in a ~780,000-line codebase). 78% of those blocks are single-line calls through C++ or C library pointers. The team expects this number to decrease as they refactor from a faithful Zig port toward idiomatic Rust.

The tools for systematic improvement — Rust's borrow checker, Miri in CI, LeakSanitizer, and continuous fuzzing — give Bun something it never had before: a way to prevent entire classes of bugs from recurring, not just fix them after the fact.

Resources