Skip to content

필사 모드: Eleven Days of Bun's Zig-to-Rust Rewrite — What Actually Transfers from a Large AI Migration

English
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

Introduction — A Project Where Writing It Up Took Longer Than the Rewrite

On July 8, 2026, Bun published Rewriting Bun in Rust. The work itself started on May 3 and was merged into main on May 14, so Jarred Sumner spent close to two months writing up something he had finished in 11 days. Simon Willison made the same observation on his own blog, noting it was a post promised since May 9.

The numbers have already made the rounds — 535,496 lines of Zig excluding comments, 1,448 .zig files, 6,502 commits, up to 64 concurrent Claude instances, roughly 165,000 dollars at API list prices. These numbers quote well, but they are not the part that transfers. What your team can actually use when it moves a legacy service next year is not "11 days" but the conditions that had to be in place for 11 days to be possible.

And those conditions are stricter than they look. This post sets Bun's announcement, Anthropic's methodology writeup, Zig creator Andrew Kelley's rebuttal, and the actual state of the repository six weeks after the merge side by side, and looks at where this approach holds and where it falls apart. The conclusion first: a rewrite that has an oracle test suite is the easiest case in all of migration. Everything else here only becomes useful once you accept that.

Eleven Days by the Numbers

The table below gathers the figures published in Bun's announcement and in Anthropic's writeup. It includes only items where the two documents state the same value; items where they diverge are called out separately in the text underneath.

ItemValue
Duration2026-05-03 to 05-14 (11 days), all platforms green at build #54202
Source size535,496 lines of Zig (comments excluded), 1,448 files, plus roughly 20 percent C++
Final diff1,009,272 lines added, 6,502 commits (6,778 including merges)
ParallelismPeak of 64 instances (4 workflows × 16), about 50 dynamic workflows in total
ModelsPrerelease Claude Fable 5, Opus 4.8 for review and rule generation
Tokens5.9 billion uncached input, 690 million output, 72 billion cached input reads
CostRoughly 165,000 dollars at API list prices
Tests0 tests deleted or skipped, 6 platforms, 57,000 to 60,000 tests per platform
unsafe shareAbout 27,000 lines of unsafe blocks out of roughly 780,000 (about 4 percent), 78 percent of them one-liners
Regressions after merge19 known, all fixed
PerformanceHTTP throughput up 2.8 to 4.8 percent, binary about 20 percent smaller

The performance row is the quietest line in this table. If 165,000 dollars buys a few percent of throughput, that on its own is not an investment case. What Bun actually bought was memory safety rather than speed — the leak benchmark cited in Anthropic's writeup has cumulative memory after 2,000 builds falling from 6,745MB to 609MB (Bun's own announcement describes the same event differently, as a 3.3MB-per-build leak disappearing. The two documents do not line up, so it is safer to treat this figure as single-source). What users felt was closer to startup time on Linux at p50 going from 517ms to 464ms, a 10 percent improvement — a result that was, in Willison's phrasing, boring in a good way.

Three Conditions That Made This Codebase Unusually Easy to Move

First, a language-independent oracle already existed. Bun's test suite is written in TypeScript. Whether the runtime is written in Zig or in Rust, not a single character of the test code changes. More than 57,000 tests per platform and over a million assertions decide whether the port is finished without any human judgment. That is exactly why it matters that zero tests were deleted or skipped — it means the judge was never touched.

How rare a condition that is becomes clear the moment you picture the opposite. A codebase whose tests are welded to the internal implementation (tests that call module-internal functions directly, or that inspect private state) has to throw those tests away the instant the language changes. A migration with no judge is just a large unverified commit. This is why Anthropic's document pins "stand up the judge first" as step 0 of six, and says to run the judge against deliberately broken code and confirm that it fails before anything else.

Second, it was a structure-preserving translation. 1,448 .zig files became that same number of .rs files. Because the architecture was not redesigned, the unit of work collapses to a single file, and knowing the file dependency graph is enough to fix the parallelization order. And what decides "is it done" is the state of the disk rather than a human call — if the output file exists, it is complete. Because the migration kit Anthropic published defines the queue this way, stopping and resuming are free.

Third, the target language's compiler was a second judge. Rust enforces ownership and lifetimes at compile time, so a sloppy translation breaks the build long before it ever reaches a test. According to the Pragmatic Engineer's writeup, roughly 1,600 compile errors were cleared in 12 hours, and that error list itself does the job of automatically writing the next work queue. A port from one strongly typed language to another gets this feedback loop attached for free. A Python-to-Python refactor has no such judge.

Compressed into one sentence, the three conditions read like this — a machine-checkable right answer already existed in two layers. The 11 days did not come from the AI being good; they came from this being a problem a machine could grade by itself for 11 days.

What Humans Still Had to Do — Fix the Loop, Not the Code

The most practical passage in the announcement is the three hours before any coding. Sumner spent about three hours discussing with Claude how Zig idioms should map onto Rust, and that conclusion was serialized into a roughly 600-line PORTING.md. The meta-rule the Anthropic kit attaches to this document is exactly right — if two agents could answer a judgment call differently, it belongs in the rulebook.

Next comes a three-file dry run. Each file gets one implementer and two adversarial reviewers, and every translation that comes out is discarded; only the rules are edited. Two problems were caught at this stage that, left alone, would have spread across all 1,448 files. Concentrating human time into three hours and three files is the core of this methodology.

Human intervention during the main run was also systemic rather than per-file. The clearest example is circular dependencies. Zig tolerates circular imports thanks to lazy compilation, and Rust's module system does not, so thousands of module errors detonated at once. What humans did here was not fix files but encode into logic how each dependency should be classified: deleted, moved, or re-bounded. A phrase Anthropic's document repeats sums up the attitude — you do not fix the code, you fix the loop that produced it.

Moved over in the barest form, the queue structure the kit recommends looks like this. The whole point is that neither a human nor a model is involved in deciding what counts as done.

# manifest.tsv: <source path>\t<target path>  (sorted in dependency graph order)
# Definition of "done" = the target file exists on disk. No other criterion is allowed.
while IFS=$'\t' read -r src dst; do
  [ -f "$dst" ] && continue
  echo "$src -> $dst"
done < migration/manifest.tsv > migration/queue.txt

wc -l < migration/queue.txt   # Work remaining. Stopping is free, resuming is just a rerun.

Why this structure matters is obvious from the opposite case. The moment you ask an agent "is this file fully ported?", progress itself becomes model output and the state of the work becomes something that can be hallucinated.

The Rebuttal — "Unreviewed Slop" and the Repository Six Weeks Later

On July 9, the day after the announcement, Zig creator Andrew Kelley published a rebuttal. The phrase The Register pulled out, "unreviewed slop," ate the headlines, but the argument he actually made is far sharper.

The core of it is a self-contradiction in the test suite. If the test suite is the grounds for landing a million lines of unreviewed code, then why did that same test suite fail to catch the bugs in the existing Zig code? Bun itself says the old code had plenty of memory bugs, so the claim that the same judge adequately verifies the new code is hard to sustain. Kelley adds that the cause lies not in the languages but in the projects' value systems — his summary line is that the core issue "has nothing to do with the language features of Zig and Rust," and everything to do with the two projects' value systems having diverged.

The point is correct, and at the same time it is missing one direction. There genuinely are classes of defect the oracle cannot catch that the target language's type system structurally eliminates — use-after-free and double-free are not expressible in the grammar once you step outside unsafe in Rust. So a large share of "bugs the tests missed" gets caught by the compiler rather than by tests. That logic weakens in proportion to the unsafe share, though, and for Bun that is about 4 percent, about 27,000 lines. A counterpoint from the HN discussion lands exactly on this spot — next to a 50,000-line Rust project with 9 lines of unsafe, 27,000 lines is not a scale you can call "safe Rust."

To be fair you also have to look at the state six weeks after the merge. When Tom Lockwood checked the repository directly on July 27, the most recent release tag was v1.3.14 from May 12, and the Rust-based v1.4.0 was still in canary as of then. Open PRs filed by bot accounts had grown from 1,277 on July 9 to 2,475 on July 27, and Lockwood estimates that adding CI costs and the token use that follows from them puts the real cost close to 800,000 dollars. That estimate is an outside observer's calculation, not a figure confirmed by Bun or Anthropic, so it is not a number to quote as-is.

The facts on the other side are just as clear. The Rust-based Bun shipped inside Claude Code v2.1.181, released June 17, and was already running for millions of people. In other words, "runs in production" and "the release is finished" were more than six weeks apart, and that gap is precisely the item that belongs in a migration plan. The merge is not the end; it is where the queue of remaining work begins.

Where This Method Breaks Down in Your Codebase

Without an oracle, it ends here. This methodology buys speed by replacing human review with machine judgment. With no judge, nothing was replaced — it was simply skipped. An example from the HN thread is a good one: to move a game engine the same way, what would you use as the judge? Systems whose output is pixels and sound do not lend themselves to portable unit tests in the first place. The same problem applies unchanged to UI layers, batch reporting, and hardware control.

Mix in a redesign and parallelism falls apart. Bun left the structure alone, which is why a file-level queue worked. If you decide to redraw module boundaries while you move, the unit of work becomes a module rather than a file, you can no longer diff source against target line by line, and the three-file dry run stops being a meaningful verification device at all. That is why the Anthropic kit sets the redesign case aside, noting that the rulebook turns into a design document and the dry run turns into a design review. The temptation to move and redraw in a single pass is the most common cause of failure for this methodology.

The review economics do not work. There is no way for humans to read a million lines. Bun accepted that and handed judgment over to adversarial reviewers, the compiler, and the tests. Whether that choice was right is honestly not settled yet — the 19 known regressions are the ones that were found, and the number that went unfound is by definition unknowable. A reaction one developer left on HN shows the tension well: if they shipped 19 regressions in two weeks, at their company they would be writing an apology to customers. On a runtime at the scale of 22 million downloads rather than 220,000, whether 19 is a big number or a small one is decided by the domain.

And a rewrite at this scale is still an exceptional choice. As Anthropic's document candidly notes, if a project like this used to be 4 years and 3 to 4 million dollars, it is now 165,000 dollars, which is why it no longer needs an existential justification. The bar has genuinely come down. But a lower bar does not by itself mean the decision is a good one. If the reason to move does not reduce to one chronic bottleneck (build times, a history of memory bugs), not moving is still the default.

Closing — Eleven Days Was a Function of the Oracle, Not of AI Performance

To summarize.

  • What produced the 11 days was not the model but three layers of machine judgment: a language-independent test suite plus file-to-file structure preservation plus a strongly typed compiler. Drop any one of the three and the same method does not deliver the same speed.
  • The time humans spent was concentrated at the front. Three hours of rulebook discussion and a three-file dry run cut off, in advance, problems that would have spread to 1,448 files. Human intervention after that was about classification rules for systemic errors, not about individual code.
  • The criticism has substance too. Justifying a rewrite with the bugs the tests missed while vouching for the new code with those same tests is an argument with a hole in it, and roughly 27,000 lines of unsafe considerably weakens any description of things having "gotten safe."
  • More than six weeks of remaining work sat between the merge and the release. Put the merge date down as the completion date in a migration schedule and that entire stretch vanishes.

A rewrite with an oracle is the easiest problem in migration. The first thing to check on your own project is not the model and not the budget, but whether you have tests that run identically before the move and after it. If you do not, the 11 days in this post are not a reference; they are a story from a different world.

References

현재 단락 (1/57)

On July 8, 2026, Bun published [Rewriting Bun in Rust](https://bun.com/blog/bun-in-rust). The work i...

작성 글자: 0원문 글자: 13,398작성 단락: 0/57