Bun Rewrites 1M Lines of Zig to Rust in 6 Days

Khanh Nguyen
Khanh Nguyen
(Updated: )
Abstract diagram showing Zig and Rust logos connected by a bridge made of green checkmarks, representing a passing test suite as the structural anchor of an agentic code port.

Bun creator Jarred Sumner used Anthropic's Claude Code to port approximately one million lines of Zig to Rust in six days, reaching 99.8% test compatibility on Linux x64 glibc. The result lives on an experimental branch and raises a specific technical question: what does that compatibility figure actually certify?

The Four-Phase Agentic Workflow That Produced the Port

The port did not happen through a single large-context translation pass. According to Sumner's public account of the process and the experimental branch on GitHub, the workflow operated across four distinct phases.

In the first phase, the agent received the existing Zig source — roughly 950,000 to one million lines — alongside the pre-existing JavaScript behavioral test suite and the C++ FFI headers for JavaScriptCore, the engine Bun embeds. The test suite functioned as the structural oracle: the port's correctness target was defined entirely by what those tests could observe.

The second phase ran hundreds of agents in parallel generating Rust source that mirrored the Zig architecture. The initial output contained more than 16,000 Rust compiler errors — lifetime mismatches, type resolution failures, and ownership conflicts — which is consistent with a direct structural translation from a language with manual memory management into one with a borrow checker.

The third phase was iterative error correction: each agent consumed rustc compiler diagnostics from stderr, applied targeted edits, and recompiled. This loop ran for the full six days. The fourth phase verified the result against the test suite and benchmarked performance against the original Zig build, which Sumner described as competitive.

The chart below maps those four phases and their primary inputs and outputs.

Bun Rust Port: Four-Phase Agentic Pipeline A left-to-right process flow showing the four phases of Bun's agentic Zig-to-Rust port: input analysis, parallel agent generation, error-correction loop, and verification. {"chartType":"pipeline-flow","title":"Bun Rust Port: Four-Phase Agentic Pipeline","summary":"Four sequential phases used to port ~1M LOC of Zig to Rust in 6 days using Claude Code agents.","data":[{"phase":1,"label":"Input Analysis","inputs":"~1M LOC Zig + JS Test Suite + C++ FFI Headers"},{"phase":2,"label":"Parallel Agent Generation","inputs":"100+ concurrent Claude Code agents"},{"phase":3,"label":"Error-Correction Loop","inputs":"16,000+ rustc compiler errors resolved over 6 days"},{"phase":4,"label":"Verification","inputs":"99.8% test pass rate on Linux x64 glibc"}]} Bun Rust Port: Four-Phase Agentic Pipeline Source: Jarred Sumner / oven-sh GitHub — agentic port methodology Phase 1 Input Analysis ~1M LOC Zig JS Test Suite C++ FFI Headers Phase 2 Parallel Gen 100+ agents Initial Rust src Phase 3 Error-Correction 16,000+ errors 6-day loop Phase 4 Verification 99.8% pass rate x64 glibc The test suite served as the sole correctness oracle throughout the migration.

Zig vs. Rust — What the Numbers Actually Show

The comparison between the original Zig build and the experimental Rust branch is useful precisely because most of the figures are concrete and directly sourced from Sumner's statements and the GitHub branch.

The codebase scale is roughly equivalent — approximately 950,000 to one million lines in Zig, and a similar count in Rust, reflecting a structural translation rather than a compression or refactor. The Rust branch resolved its initial 16,000-plus compiler errors to zero by day six. Test compatibility sits at 99.8% on Linux x64 glibc, which Sumner described as the primary verification target. Performance, according to his account, remains competitive with the Zig original, though no specific benchmark figures were provided in the source material.

The one figure with no clean analog in the Zig original is the 14,000 unsafe blocks in the Rust port. Zig does not have an unsafe keyword in the same sense — the entire language operates with explicit allocator control and no borrow checker — so this number reflects the cost of interfacing with JavaScriptCore's C++ layer and of carrying over Zig's global mutable state patterns into a language that requires explicit annotation when those patterns are used. The chart below shows the key metrics side by side.

Bun Zig vs. Rust Branch: Key Metrics Compared A side-by-side metric comparison of the original Zig build and the experimental Rust branch across codebase size, test compatibility, and unsafe block density. {"chartType":"metric-cards","title":"Bun Zig vs. Rust Branch: Key Metrics Compared","summary":"Direct comparison of key measurable attributes between Bun's Zig original and the Claude-generated Rust experimental branch.","data":[{"metric":"LOC","zig":"~950k–1M","rust":"~960k+"},{"metric":"Compiler Errors (initial)","zig":"0 (stable)","rust":"16,000+"},{"metric":"Test Compatibility","zig":"100%","rust":"99.8% (Linux x64 glibc)"},{"metric":"Memory Model","zig":"Manual allocators","rust":"RAII + Borrow Checker"},{"metric":"Unsafe Density","zig":"N/A (manual by nature)","rust":"~14,000 unsafe blocks"}]} Bun Zig vs. Rust Branch: Metrics Zig (Main — Stable) Rust (Experimental Branch) Codebase Size ~1M LOC ~960k+ LOC Test Compatibility 100% 99.8% Memory Model Manual allocators RAII + Borrow Checker Unsafe Blocks N/A (manual) ~14,000 blocks

The Unsafe Block Problem and What a Test Suite Cannot Certify

The 14,000 unsafe blocks deserve direct attention, because they mark the boundary of what the test suite can and cannot verify.

Rust's unsafe keyword does not mean "broken." It means the programmer — or in this case, the agent — has asserted that a block of code upholds memory and concurrency invariants that the borrow checker cannot prove mechanically. The two primary sources of these blocks in this port are the FFI layer connecting Rust to JavaScriptCore's C++ API, and the global mutable state inherited from the Zig architecture. Both are structurally expected when porting a system that embeds a C++ engine and was not originally written with Rust's ownership model in mind.

What the existing JavaScript test suite can certify is behavioral correctness at the runtime's public interface: that a given script produces the expected output, that timers fire correctly, that modules resolve as expected. What it cannot certify is whether any of those 14,000 unsafe blocks uphold the memory invariants they claim to. A use-after-free in an infrequently reached FFI path, or a data race in a threading edge case, may not surface in a behavioral test run at all — it may surface under load, on a specific platform, or not until production.

This is not unique to agentic code. It is the standing challenge of any large unsafe Rust codebase. But it is worth naming precisely because the 99.8% test compatibility figure has been cited in community discussion as the primary evidence of the port's soundness. That figure is evidence of behavioral compatibility. It is not evidence of memory safety, which is the property the language was chosen to improve.

Sumner cited memory leaks and crashes in the Zig implementation as the primary reason for the rewrite. Whether the Rust port resolves those failure modes depends on whether the unsafe blocks are correct — a question the test suite cannot answer alone. The chart below maps the three main sources of unsafe blocks and the verification gap each represents.

Bun Rust Port: Sources of Unsafe Blocks and Verification Limits A three-column breakdown of the primary unsafe block sources in the Rust port — FFI layer, global mutable state, and concurrency — and what the JS test suite can and cannot verify in each case. {"chartType":"constraint-cards","title":"Bun Rust Port: Sources of Unsafe Blocks and Verification Limits","summary":"The ~14,000 unsafe blocks arise from three structural sources; the JS behavioral test suite verifies runtime output but cannot certify memory correctness in any of them.","data":[{"source":"FFI / C++ JavaScriptCore","verified_by_tests":"Runtime output","not_verified":"Memory lifetime across FFI boundary"},{"source":"Global Mutable State","verified_by_tests":"Observable state outcomes","not_verified":"Data race absence under concurrency"},{"source":"Manual Thread Management","verified_by_tests":"Functional threading behavior","not_verified":"Send/Sync invariant correctness"}]} ~14,000 Unsafe Blocks: Limits of Verification JS behavioral tests verify runtime output — not memory lifetime or concurrency invariants FFI / C++ JSC Rust must call C++ APIs with no borrow checker protection across the language boundary. Not Certified Memory lifetime across FFI boundary Global Mutable State Zig-era globals ported directly require unsafe annotations in Rust's ownership model. Not Certified Data race absence under real concurrency load Thread Management Zig-style threading ported without full adoption of Rust's Send / Sync trait system. Not Certified Send/Sync invariant correctness at runtime

What the Port Does and Does Not Settle About Agentic Large-Scale Migration

The six-day timeline is the detail that has drawn most of the external commentary, including references in Hacker News discussion to an "Inverse Hofstadter's Law" — the idea that LLMs may complete certain mechanical translation tasks faster than conventional human estimates would suggest.

That framing is accurate for the mechanical dimension of the task. Translating function signatures, rewriting allocator calls, and resolving type mismatches at scale is exactly the kind of high-volume, locally constrained work where parallel agentic loops can outperform sequential human development. The error-correction loop — consuming rustc diagnostics and re-editing — is a deterministic feedback signal, which is a favorable condition for agent-driven iteration.

What the port does not settle is the maintainability question. The Bun codebase, even in its Zig form, was already noted for tensions with upstream tooling, including a disagreement with the Zig core team over a parallelization pull request significant enough that Bun maintains a custom Zig fork. A codebase of this scale, now containing roughly 14,000 unsafe blocks generated by an agent over six days, presents an open question about whether the developer who did not write each block in the traditional sense can reason about the invariants each block assumes. This is sometimes described in systems software discussions as "cognitive debt" — the gap between what a codebase does and what its maintainers can mentally model.

The model or models used for the port have not been officially confirmed beyond Sumner's public references to Claude Code. Community discussion has suggested the involvement of higher-compute internal models, but that remains unverified and is noted here only to characterize the state of public information accurately. The experimental branch is real and publicly accessible. Whether it becomes the production runtime depends on what a more thorough audit of the unsafe surface reveals — a question that goes beyond what the test suite can answer.

Comments (0)

No comments yet.

Be the first to share your perspective on this topic.