The Zig Creator is NOT Happy About This... (Bun to Rust)

BBetter Stack
Computing/SoftwareSmall Business/StartupsManagement

Transcript

00:00:00Bun rewrote its entire codebase from Zig to Rust in just 11 days, and that version will soon be released into production.
00:00:06Now that on its own is a crazy story, but then Andrew Kelly, the creator of Zig, wrote a blog post,
00:00:11and let's just say, he does not agree that this was a technical decision.
00:00:14There are accusations of hacks on top of hacks, Jared's management style, and a line about tasteless AI enthusiasts.
00:00:20Let's grab the popcorn.
00:00:26So Bun version 1.3.14 is going to be the last release written in Zig, and version 1.4 will ship with the new Rust codebase,
00:00:33which is a port that happened in only 11 days between May 3rd and May 14th,
00:00:37and contains 6,502 commits with around a million lines of code added.
00:00:42That is one hell of a PR review.
00:00:44Now obviously they used AI to do this, and at that peak they had 64 clawed instances running simultaneously across 4 Gitwork trees,
00:00:51with a peak of 58 commits in 1 minute, all on a pre-release version of Fable 5.
00:00:55All in all, they estimate that the total price of this rewrite if they had used the API would be $165,000,
00:01:01and we recently passed 165,000 subscribers, so if you haven't yet, you should subscribe too.
00:01:06So why did they actually rewrite Bun?
00:01:08I mean, Zig is a very good language.
00:01:10Well, the main reason that Jared gives is stability.
00:01:13Bun's whole thing is about being fast, and up until now they've been using Zig with manual memory management,
00:01:17no automatic cleanup, constructors or destructors, and no borrow checker.
00:01:21In Zig, cleanup is expected to be written out explicitly at each cool site with the fur.
00:01:25The problem with that is that Bun sits on top of JavaScript core, which is garbage collected,
00:01:29so you end up with garbage collected JavaScript values constantly crossing into manually managed Zig memory.
00:01:34This is just a snippet of a few of the stability-related bugs that they fixed in the last version of Bun,
00:01:38and essentially, they had had enough.
00:01:40In Zig, memory safety was enforced by style guides and code review.
00:01:43In Rust, the borrow checker would enforce it at compile time,
00:01:46and as Jared says, a large percentage of these bugs were used after free, double free, and forgot to free in an error path,
00:01:52and in safe Rust, these are all compiler errors, and compiler errors are a better feedback loop than a style guide.
00:01:57That is the core reason for this pull.
00:01:59So did he just open up Claude code in the Bun repo and say port this to Rust and make no mistakes?
00:02:04Well, obviously not.
00:02:05They first had two questions they needed to answer.
00:02:07First, do they pull it all at once?
00:02:09And second, how do they keep Bun in Rust the same as Bun in Zig?
00:02:13For that first question, the answer is yes, they're going to do all of it at once,
00:02:16as an incremental rewrite adds temporary code that you hope gets deleted eventually,
00:02:20but sometimes doesn't, and it would be painful in the short-medium term.
00:02:24For the second question, they did choose to do a faithful rewrite rather than an idiomatic rewrite,
00:02:28so the Rust code deliberately preserves the Zig architecture,
00:02:31and you can see this in a lot of the provided code snippets.
00:02:34The functions flow and read nearly identically.
00:02:36This is actually the same way that the TypeScript team handled the Go rewrite,
00:02:39although that one I don't think they had access to Fable for.
00:02:42With those questions answered then, the next step was to get going with the port.
00:02:45First, they wrote up a document called porting.md,
00:02:48which mapped out the Zig patterns to their Rust equivalents,
00:02:50and this was actually one of the first signs that Bun was considering this move,
00:02:54and it got trending on Twitter and Hacker News when people noticed this on GitHub.
00:02:58Funnily enough, Jared actually replied on Hacker News,
00:03:00saying this whole thread is an overreaction.
00:03:02302 comments about code that does not work.
00:03:04We have not committed to rewriting.
00:03:06There's a very high chance all of this code gets thrown out completely.
00:03:09It seems that did not happen.
00:03:10After the porting guide, they also had a file called lifetimes.tsv that specifies the intended
00:03:15lifetime of struct fields, and this was actually done using a prompt like this in Claude.
00:03:19With those files set up, they were ready to spin up their AI agents and begin with a trial of three
00:03:23files, and the workflow they settled on has one implementer agent writing the new Rust file,
00:03:27and two adversarial review agents checking over it, and these were actually separate Claude instances
00:03:32that only received the diff, and after this, a fixer agent would then apply their suggestions.
00:03:37I guess everything went pretty well in the three-file test, as Jared then asked Claude to loop the
00:03:41workflow on all 1,448 zig files.
00:03:44That didn't go completely smoothly though, as about two minutes in, one Claude ran git stash before it
00:03:49committed, and another ran git stash pop, and then git reset, so each agent was fighting with each other,
00:03:54and if Jared had used separate work trees for all of these, he would actually run out of disk space,
00:03:57because the bund git repository is so big, and eventually the changes do need to be compiled and seen together.
00:04:03To fix this, Jared simply asked Claude to not run git stash, or any git command that didn't relate to
00:04:07committing a file, along with no cargo commands, or any command that was basically slow.
00:04:11After this slight modification, Claude resumed the workflow, and everything was working well, just pretty slowly.
00:04:17So Jared split it out into four workflow shards, each with their own work tree, so we had four work trees in
00:04:21total, each running 16 Claudes, committing and pushing the files.
00:04:25As I said earlier, this parallelization and prep work meant that at its peak, Claude wrote about
00:04:291,300 lines of code per minute, and every line of code was reviewed by two separate adversarial
00:04:34reviewers, and going through a round of fixes before committing.
00:04:37Still though, absolutely none of this worked yet.
00:04:40So the next stage was going to be fixing the compiler errors, going crate by crate.
00:04:43Now for a quick bit of context here, the old zig codebase was effectively one big compilation
00:04:48unit, and Jared wanted the Rust version split into 100 crates so it would compile faster.
00:04:53The problem with this is that crates can't have cyclical dependencies, and Zig never cared about
00:04:57that, so their code was full of them.
00:04:59Jared actually tried to solve this himself in a PR right before the rewrite started, but
00:05:02it just wasn't enough, so instead he ran one workflow to classify where all the cyclical
00:05:06code should live and write it down, and then another workflow to actually do the refactor.
00:05:10Fixing these cycles then revealed about 16,000 compiler errors.
00:05:14As Jared puts it, that is a massive number for one human, but not actually a crazy number
00:05:18for 64 clauds.
00:05:19So another workflow was kicked off, this time looping over each crate, running cargo check
00:05:23once at the start, grouping errors by file and saving them, and then having Claude fix
00:05:27all the errors within that crate, having the two adversarial reviewers again to check the changes,
00:05:31and one fixer to apply the suggestions.
00:05:34To stop the Claude stepping on each other again, cargo check was only run at the very beginning,
00:05:38and no git until the end.
00:05:40This had its own false start though, as Claude actually interpreted the instruction of
00:05:43let's get all these crates to compile, as stub out the functions that have errors.
00:05:46I mean, that does technically compile.
00:05:48It also started writing suspiciously long comments explaining why its workarounds were
00:05:52fine, so Jared added a rule for the adversarial reviewers saying if you need a paragraph long
00:05:56comment to justify why the workaround is okay, the code is wrong, fix the code.
00:06:01Honestly, that is a pretty good rule of thumb.
00:06:03After a while, that workflow had fixed all of the compiler errors, so the next step was
00:06:06simply getting the bun version command to run, and this had a few issues, but I guess
00:06:10everything worked in the end, so the next goal was simply running a test on a single file.
00:06:14This was yet another Claude workflow, so they loop over every bun CLI subcommand, save
00:06:18each failing stack trace to a file, one Claude fixes it, two adversarial reviewers, and then
00:06:23one fixer for those reviewers.
00:06:24They seem to be really confident and having good success using this workflow shape with Fable,
00:06:28so I might actually have to try this out one day.
00:06:30Once they have the CLI commands working, it meant they can now run the full bun test suite,
00:06:33and this workflow actually ran 100 random test files at a time, charted across the four
00:06:37work trees by folder, fixing failures with the same review loop.
00:06:40This was actually a massive challenge, as Bun's test suite is pretty comprehensive.
00:06:44They have memory leak tests, integration tests that run next dev, and check that hot reloading
00:06:48picks up changes 100 times.
00:06:50They have stress tests that exhaust every TCP socket on the machine, tests that write gigabytes
00:06:54to disk, and tests that spawn around 10,000 processes.
00:06:58Even with isolation, CPU, and memory limits, the machine still ran out of disk space and crashed
00:07:02several times.
00:07:03Regardless of that though, two days later the first CI run was done, and the failing test list
00:07:07was down from 972 test files to 23.
00:07:10A day and a half later after that, Linux went fully green across 60 shards, followed by macOS
00:07:15and then Windows, and on May 14th, build 54,202 went green on all six platforms.
00:07:21The rewrite had seemingly been a success.
00:07:24Next, all Jared needed to do was manually check that the tests were actually running and
00:07:27it hadn't skipped any silently, and also run a bunch of stuff locally, and then press the
00:07:31merge button.
00:07:32The final build for all of this fable usage was 5.9 billion uncashed input tokens, 690
00:07:37million output tokens, and 72 billion cached token reads.
00:07:41As I said, that is around 165,000 API tokens, but Jared estimates doing this by hand would
00:07:46have taken three engineers, the full context of the codebase for about a year, during which
00:07:50they wouldn't be able to fix bugs or ship features, so realistically they just never would
00:07:54have done it.
00:07:54This does actually seem like it's the first large-scale example of breaking the never-rewrite
00:07:58rule, and as Jared says, until very recently, programming language choice was a one-way decision.
00:08:03You pick your language on day one, and you're stuck with that for the life of the project.
00:08:07Now though, if AI agents can do a faithful, test-validated port of half a million lines
00:08:11for $165,000 in under two weeks, that stops being true.
00:08:15And yes, that number is a lot of money for you or me, but for a company, it's a fraction
00:08:19of what a small team costs for the year that that same port would take.
00:08:23So that is how Bun was rewritten in Rust, but did they actually get any benefits to this
00:08:27rewrite? Well, the binary is about 20% smaller, thanks to better code gen and linker optimizations,
00:08:32it's 2-5% faster across the HTTP server and build benchmarks, largely from the cross-language
00:08:37link time optimization, and memory leaks are dramatically down.
00:08:40For example, repeatedly calling bun.build used to leak about 3MB per call, but now it's
00:08:45constant.
00:08:45They also fixed 128 known bugs in this process, but at the same time they did ship 19 new regressions
00:08:50from the port, but those have since been fixed.
00:08:53Apparently about 4% of Bun's Rust code sits inside an unsafe block as well, which is around
00:08:5713,000 unsafe keywords, but 78% of those blocks are a single line, usually just a pointer coming
00:09:03back from C++.
00:09:04This is actually one of the trade-offs that they went with when they started this port.
00:09:07They started off with unsafe keywords while they ported over all of the functions line
00:09:11by line, but then they can work on removing these over time once they're only dealing with
00:09:14that Rust code base.
00:09:16Overall, this is going to be an ongoing piece of work, but it does seem to have worked.
00:09:19Claude Code actually runs the Rust port of Bun, and they saw startups being 10% faster on
00:09:24Linux, and basically no one noticed that they switched over, which is incredibly impressive
00:09:28as this is a super large-scale in-production test.
00:09:31But now it's time for some drama, because a few days after this Bun post, Andrew Kelly,
00:09:35the creator of Zig, published my thoughts on the Bun Rust rewrite.
00:09:39His core argument is that this was never really a technical decision, in telling it's a relationship
00:09:43breakdown, and he says that Bun's code base had become hacks on top of hacks, that features
00:09:47were shipped recklessly without paying down bugs, and that the Zig team had actually started
00:09:51to see Bun as a net liability for the language.
00:09:53He says, along with the discomfort of Bun being the publicly presumed poster child for
00:09:57the Zig programming language, it was actually being the prime example of how not to write
00:10:01Zig code.
00:10:01Ouch.
00:10:02He then also went after that leadership culture too, showing a tweet from when Bun was hiring,
00:10:06saying if work-life balance means a lot of time spent not working, it's probably not
00:10:09a good fit.
00:10:10He claims that this reputation actively hurt their recruitment, and that people in the Zig community
00:10:14were steering clear of the company, and I quote here,
00:10:17Jared was a stinky manager, poor communication, unrealistic expectations, low empathy, no
00:10:22experience, just a total shitshow.
00:10:24Yeah, I get the sense that he does not like Jared.
00:10:27He then also disputes the fact that the Rust port was necessary, pointing out that the binary
00:10:30size wins and link time optimizations were implementation choices that were available
00:10:34in Zig the entire time, Bun just never did the work.
00:10:38He says that the Bun blog frames it that you either have to choose a style guide or a programming
00:10:41language feature in order to avoid these bugs, misdirecting the reader away from the main
00:10:45way that bugs are eliminated by dedicating engineering resources to it.
00:10:48You're not giving Tiger Beetle nearly enough credit.
00:10:50Quite simply, they put in the time to find and eliminate the bugs, they make an effort to
00:10:54maintain a healthy relationship with ZSF, and Bun did neither of those things.
00:10:58He goes on to say that the argument for shipping a million lines of unreviewed code is that the
00:11:02test suite is good enough to catch everything, but then why are you saying that you have so
00:11:05many annoying bugs in Zig?
00:11:07Wham!
00:11:07After this, and yes, he is still going, he then accuses them of lying about the advantage
00:11:19that Rust had to the binary size, saying all that engineering work had nothing to do with
00:11:22the rewrite.
00:11:23I think this is precisely why it took so long for the blog post to come out.
00:11:26You were doing the engineering work that you should have done in the Zig code base since
00:11:29the beginning.
00:11:30We've been trying to warn you about your comp time of use for years.
00:11:32We even made this time report thing specifically for projects that need to audit their usage
00:11:36of comp time, inline, usage, and compile times.
00:11:39He then claims that they purposely left out the compile time comparison in the blog, saying
00:11:43I'm clocking 16 seconds to build from scratch with clean cache, followed by 90 milliseconds
00:11:47for each subsequent edit with incremental compilation enabled, or the corresponding measurements
00:11:51of Bun post rewrite.
00:11:52And if you've used Rust before, you can probably guess it's going to be a lot longer than 16 seconds.
00:11:56All of that was just his response to the blog post, and we're still not done yet.
00:11:59He then goes on to say that the association with Anthropic brought drive-by-slop contributions
00:12:03and tasteless AI enthusiasts into the Zig community, and that he's relieved that the connection
00:12:08is severed, and he had feared Zig's identity would become known as a programming language
00:12:11associated with AI.
00:12:13He also says the blog post is expertly written, it's almost like the marketing department of
00:12:17a trillion dollar company has a lot of money riding on this, implying that Anthropic
00:12:21in some way want to use this as the poster child of Fable's capabilities.
00:12:25And I mean, yeah, they probably do.
00:12:26The blog post that you can actually find on his site now has been rewritten, and he does
00:12:30admit to this, saying that he updated the conclusion section after self-reflection and chatting
00:12:33with friends, but he didn't actually make this nicer.
00:12:36In fact, quite the opposite.
00:12:37The original ending was What Did We Learn Here Today, where he claimed that I actually don't
00:12:40have any personal criticisms of Jared and signed off with a shrug emoji, but that has now
00:12:44been replaced with a section called Moving On, where he admits I resent Jared for making
00:12:48Bun into an embarrassment for Zig, and I blame him partially for some of the slop we've
00:12:52been dealing with, and I stand by my criticism of his leadership.
00:12:55He also apologizes to Zig users who saw the language's creator trashing an ex-user and
00:13:00worried that they would be the next target, asking for grace with the line, I hope you
00:13:03can give me some grace, considering a trillion dollar company fired the first shot.
00:13:07Honestly, I think both of these posts hold some element of truth.
00:13:10Kelly is right that nothing about the Rust rewrite was strictly necessary.
00:13:14Bun could have invested in memory-safe discipline, tooling, and LTO in Zig, but Jared is also
00:13:18right that after years of use-after-free bugs, we could have been more disciplined is not
00:13:22a valid strategy, so a compiler that makes that bug class impossible is going to be a style
00:13:27guide.
00:13:27As for the AI side, I'd say this was not magically AI rewrote Bun, it was a good engineer designing
00:13:32a translation pipeline, with porting documentation, lifetime specs, and adversarial reviewers, plus
00:13:371.3 million test assertions.
00:13:39If anything, I just think this whole thing shows us how important tests can be to an AI
00:13:43agent, and in fact it's the reason why we saw a wave of projects removing that test from
00:13:46the public, as AI was very good at working until those tests pass.
00:13:50I mean, it's what Cloudflare did with Next.js, for example.
00:13:53So that's the full story.
00:13:54Bun is now written in Rust, and Zig lost one of its flagship projects, but its creator seems
00:13:58pretty happy about it.
00:13:59I'd love to know what you think.
00:14:00Does this only work if Anthropic is paying for your token bill?
00:14:03Do you trust Bun now?
00:14:04Let me know your thoughts in the comments down below, or there's subscribe, and as always,
00:14:07see you in the next one.

Key Takeaway

Bun successfully executed a large-scale, AI-assisted migration from Zig to Rust to solve persistent memory-safety issues, despite the creator of Zig dismissing the technical necessity of the shift and highlighting underlying organizational friction.

Highlights

  • Bun migrated its entire codebase from Zig to Rust in 11 days, resulting in version 1.4.

  • The migration involved 6,502 commits, the addition of approximately 1 million lines of code, and an estimated $165,000 in AI API usage.

  • The transition uses a workflow involving 64 concurrent Claude instances across four Git worktrees, utilizing separate implementer, adversarial reviewer, and fixer agents.

  • The resulting Rust codebase demonstrates a 20% smaller binary size and 2-5% faster performance in HTTP server and build benchmarks.

  • Andrew Kelly, the creator of Zig, publicly criticized the move, arguing the rewrite was unnecessary and stemmed from poor leadership and engineering practices rather than technical limitations of Zig.

  • Approximately 4% of the new Rust code relies on unsafe blocks, mostly handling C++ pointer interactions, with plans to reduce this over time.

Timeline

The Migration Process

  • Bun transitioned from Zig to Rust for version 1.4 to gain compile-time memory safety via Rust's borrow checker.
  • The project employed a massive parallel workflow using 64 AI agent instances to rewrite code.
  • Architectural fidelity was prioritized by mapping Zig patterns to Rust equivalents rather than rewriting for idiomatic Rust.

Bun moved away from Zig due to stability concerns, specifically memory-related bugs like use-after-free and double-free errors that occurred when crossing between garbage-collected JavaScript and manually managed Zig memory. To ensure stability during the transition, the team implemented a strict workflow where one agent wrote code, two adversarial agents reviewed the changes, and a final agent applied fixes. This process was managed across 64 concurrent instances to handle the codebase size within an 11-day timeframe.

Technical Challenges and Execution

  • Cyclical dependencies in the original Zig code required a classification and refactoring workflow before the Rust conversion.
  • The team utilized a test-driven approach, running thousands of test assertions to validate the AI-generated code.
  • Massive resource consumption, including 5.9 billion input tokens, was required to complete the migration.

To address the complex dependencies, the team broke the codebase into 100 crates. During the porting, they encountered 16,000 compiler errors and numerous test failures. They resolved these issues by iterating through subcommands and test files, using AI to fix failures while maintaining a strict review loop. The final successful build confirmed that the AI-led approach could handle complex software engineering at scale.

Post-Migration Results and Community Conflict

  • Post-rewrite, the binary size decreased by 20% and performance improved by 2-5%.
  • Zig's creator, Andrew Kelly, argued the rewrite ignored existing solutions within Zig and represented a failure in management.
  • The disagreement centered on whether the rewrite was a technical necessity or a consequence of ignoring long-standing engineering discipline.

The rewrite successfully improved memory safety and performance, though it introduced minor initial regressions. Andrew Kelly countered that these benefits were achievable within Zig if proper engineering resources had been applied earlier. His critique also addressed cultural issues within the Bun team and expressed relief at separating Zig's identity from Bun, which he characterized as a poor example of Zig usage.

Community Posts

View all posts