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.