99.8% Test Coverage, 0.2% Undefined Behavior: The Day AI Wrote a Million Lines of Rust
Let me tell you about the most impressive and deeply concerning thing I’ve seen in systems programming this year.
On May 14, 2026, the Bun team merged PR #30412: a complete rewrite of the entire Bun JavaScript runtime from Zig to Rust. One million lines of new code. Four thousand lines deleted. Six days. Two developers (Jarred Sumner and Dylan Conway). And a cast of AI co-authors that reads like a sci-fi screenplay: autofix-ci[bot], robobun, and claude[bot] all listed as contributors.
The rewrite passes 99.8% of Bun’s pre-existing test suite. The binary is 3-8 MB smaller. Benchmarks are neutral to faster. And on May 15 — literally the day after merge — someone opened Issue #30719 with the subject line: “all of rust codebase: This codebase fails even the most basic miri checks, allows for UB in safe rust.”
(As of writing, the rewrite is in canary — bun upgrade --canary — and still needs optimization and cleanup before landing in a stable release. But the fact that it works at all is the remarkable part.)
The plea in that issue was simple and devastating: “Please consider not vibe coding Rust. AIs are not good at writing Rust. Also hire a real Rust dev.”
The story blew up fast: 717 points and 694 comments on Hacker News, 689 points and 769 comments on the PR itself. The Register ran with “Anthropic’s Bun Rust rewrite merged at speed of AI.” The internet has opinions.
I’m going to be honest with you: they’re right. And also wrong. And also right again. Let me explain.
The Setup: Why Zig Was Never Going to End Well
To understand how we got here, you need to understand how Bun got to Zig in the first place. Bun was written in Zig because Zig was the new shiny thing that promised C-compatible performance without C’s memory unsafety. And for a while, it worked.
Then it didn’t.
Bun had an extremely high rate of crashes and memory bugs — the kind of issues that Zig’s “you’re an adult, handle it” philosophy doesn’t actually prevent. The team forked Zig to get 4x faster compilation times, landing at oven-sh/zig with 11 commits ahead of main. Zig’s maintainers rejected the fork changes as “low quality” and “undesirable.”
Bun’s response? “We do not currently plan to upstream this, as Zig has a strict ban on LLM-authored contributions.”
Which is rich, because Zig’s anti-AI policy wasn’t actually the reason they rejected the PRs. A core maintainer clarified this. But the policy became the convenient scapegoat, and suddenly the Zig vs. Rust debate was on again.
Meanwhile, Anthropic had bought Bun. And Anthropic has, you know, AI.
The Rewrite: 1,009,257 Lines in 6 Days
Here’s what happened: Jarred Sumner and Dylan Conway, with heavy AI assistance, rewrote the entire codebase. Same architecture. Same data structures. Few third-party libraries. No async Rust (because apparently even AI-assisted rewriting has limits). The code was reviewed by Claude and CodeRabbit bots, which found documentation inconsistencies, hardcoded paths, and potential null references. The rewrite also fixed several memory leaks and flaky tests that had plagued the Zig version for years.
The result? A working, faster, smaller JavaScript runtime written in a language that — let’s be honest — most of the Bun team had never written production Rust in before.
This is either the future of software engineering, or the most expensive demo ever. I think it’s both.
The Bug That Proves Everything
Here’s where the story gets interesting. And by “interesting,” I mean “the kind of thing that keeps systems engineers up at night.”
The issue reporter found that PathString::init — a function that creates a PathString from a byte slice — was erasing the slice’s lifetime using transmute, making it appear as if the data lived for 'static when it absolutely didn’t.
Let me show you what the AI generated:
struct PathString {
slice: &'static [u8],
}
impl PathString {
fn init(str: &[u8]) -> PathString {
PathString {
slice: unsafe { std::mem::transmute(str) },
}
}
fn slice(&self) -> &[u8] {
self.slice
}
}
fn main() {
let test = Box::new(*b"Hello World");
let init = PathString::init(&*test);
drop(test);
println!("{:?}", init.slice()); // UB: reading freed memory
}
This compiles. It runs. And it produces garbage output because init.slice() returns a dangling reference to freed memory. All through safe Rust.
The AI translated Zig’s “pointer is a pointer” semantics directly to Rust by using transmute to erase lifetimes. This is exactly the kind of bug that Rust’s type system is designed to prevent — except the AI didn’t understand why Rust has lifetimes. It just translated patterns mechanically.
Miri caught it immediately:
error: Undefined Behavior: constructing invalid value of type &[u8]:
encountered a dangling reference (use-after-free)
--> src/main.rs:31:9
|
31 | self.slice
| ^^^^^^^^^^ Undefined Behavior occurred here
The fix, merged in PR #30728, made init an unsafe fn with a documented outlives contract. This is a pragmatic compromise for a 1M-line rewrite — threading proper lifetimes through all ~70 call sites would have been a massive undertaking. But it also means the compiler no longer enforces the safety guarantee. The programmer must.
What This Actually Means
Let me be clear about what I think happened here, because the internet is going to get this wrong either way.
Rust’s tooling caught a bug that Zig would have let through silently. That’s the headline. In Zig, this code compiles, runs, and silently corrupts memory. In Rust, Miri caught it on day one. The borrow checker rejected the naive fix at compile time. Clippy is presumably already complaining about things we haven’t even found yet.
But AI-generated Rust is not automatically correct. The AI used transmute to erase lifetimes, creating unsound safe functions. The borrow checker can’t reason about unsafe blocks — it trusts the programmer. And the AI, as the issue reporter noted, is not a programmer.
The 99.8% test coverage is impressive but misleading. Passing tests doesn’t mean your code is correct. It means your code does what the tests expect. Undefined behavior in safe code is the kind of thing that passes every test until it doesn’t — and when it doesn’t, it doesn’t in production at 3 AM on a Saturday.
The Honest Truth About AI-Assisted Systems Programming
I’ve been writing systems code for long enough to remember when “let’s just use C” was the answer to everything. Then we learned that C is fine until it isn’t, and then you spend six months debugging a use-after-free that only happens when the moon is full.
Rust was supposed to be the answer. And for the most part, it is. But Rust’s safety guarantees only work if someone actually understands them. You can’t transmute your way out of a lifetime problem and call it a day. You can’t AI-translate a pointer from one language to another and expect the borrow checker to save you.
The Bun rewrite is a case study in this tension. On one hand: a million lines of production-quality systems code, rewritten in a new language, in six days, with AI assistance, and it mostly works. That’s genuinely impressive. On the other hand: the code has undefined behavior in safe functions, caught by Miri on the first day, because the AI didn’t understand Rust’s aliasing rules.
Both things are true.
The Takeaway
Here’s what I think, and I’ve thought about this a lot:
Rust is the right choice for Bun. The team had memory bugs in Zig. Rust’s borrow checker, Miri, and Clippy give you tools to find those bugs before they reach production. That’s not ideology — that’s engineering.
AI is a force multiplier, not a replacement for understanding. The AI wrote the code. But it didn’t understand the code. The Miri issue reporter was right: AIs are not good at writing Rust. They’re good at writing Rust-like code. There’s a difference.
Miri is not optional. If your codebase has unsafe blocks (and any codebase with transmute does), you need Miri in your CI. The Bun rewrite passed 99.8% of its test suite but had UB in safe code — the kind of bug that only Miri catches.
The future is here, and it’s messy. Six days to rewrite a million lines of code is not something we’ll see again. But it’s also not something we should blindly replicate. The Bun team has a test suite that catches almost everything. Most teams don’t. Most teams would have shipped something that works until it doesn’t.
Final Thoughts
I’ll admit something: when I first heard about the rewrite, I was skeptical. A million lines in six days? Using AI? In Rust? My first thought was “this is going to be a disaster.”
I was partially right.
The code has bugs. Real bugs. Undefined behavior in safe code. The kind of bugs that make you question your life choices when you find them at 2 AM.
But here’s the thing that surprised me: the bugs were caught. By Miri. On day one. In Zig, these bugs would have been in production for months, manifesting as intermittent crashes that nobody could reproduce. In Rust, they were caught by a tool before anyone even ran the code.
That’s the difference. Rust doesn’t prevent all bugs. But it catches the ones that matter most, and it gives you tools to find them before they reach production. Zig relies on the programmer to be perfect. Rust gives you a safety net.
The safety net isn’t perfect. The AI fell through it. But the net is there, and it caught the fall before it became a freefall.
I think that’s worth something.
The code examples in this post are simplified versions of the actual bugs and fixes from Bun’s rewrite. The full details are in Issue #30719 and PR #30728. If you’re writing systems code in Rust and not running Miri in your CI, you’re already behind.