needhelp
← Zurück zum Blog

Buns Rust-Umschreibung: 11 Tage, 6.778 Commits und was wirklich geändert hat

von needhelp
Bun
Rust
Zig
JavaScript Runtime
LLM
Software Engineering

Das Bun-Team lieferte im Mai 2026 etwas Ungewöhnliches: Es ersetzte den gesamten nativen Kern der Runtime durch einen Port in Rust — und das in 11 Tagen.

Der Hinweis am Anfang der Ankündigung setzt den Ton. Bun wurde im Dezember 2025 von Anthropic übernommen. Jarred Sumner, der Schöpfer von Bun, nutzte eine Vorabversion von Claude — ein „Modell der Mythos-Klasse“, das sie Fable 5 nennen — für einen Großteil der Arbeit. Das ist kein Gedankenexperiment. Bun v1.4.0, die erste Rust-Version, ist jetzt im Canary.

Die nackten Zahlen:

  • 535.496 Zeilen Zig (ohne Kommentare) wurden zu einem Rust-Codebase
  • 6.778 Commits zwischen dem 3. und 14. Mai
  • 1.448 .zig-Dateien mechanisch nach .rs portiert
  • ~165.000 $ API-Ausgaben (5,9 Mrd. ungecachte Eingabetokens, 690 Mio. Ausgabetokens)
  • Spitze: 64 Claudes gleichzeitig über 4 Worktrees

Bun-Start nach der Rust-Umschreibung, ausgeführt unter Claude Code

Bun war immer eine Zig-Wette

Bun begann als zeilenweiser Port des JS/TS-Transpilers von esbuild, von Go nach Zig. Sumner schrieb seine erste Zeile Zig am 16. April 2021, nachdem er die einseitige Zig-Sprachreferenz auf Hacker News gesehen hatte. Die erste Version — Transpiler, Minifier, Bundler, npm-kompatibler Paketmanager, Jest-artiger Test-Runner, Node.js-API-Fläche — schrieb eine einzige Person in einem Jahr, in einer engen Wohnung in Oakland, bevor LLMs für diese Art Arbeit taugten.

Die Wette ging auf. Buns CLI sieht jetzt über 22 Millionen Downloads pro Monat. Claude Code und OpenCode nutzen es als Runtime. Vercel, Railway und DigitalOcean liefern erstklassigen Erstparteien-Support.

Der Umfang war zugleich das Problem.

Die technische Schuld: GC-Speicher neben manuellem Speicher

JavaScript ist garbage-collected. Bun bettet JavaScriptCore (die Engine in Safari) plus einen Haufen C/C++-Bibliotheken ein: uWebSockets, BoringSSL, SQLite, lsquic. Zig verwaltet den Speicher, wie C, nicht für dich. Buns Aufgabe ist es, zwischen einer GC-Sprache und manuell verwaltetem nativem Code zu stehen, und genau an dieser Grenze lebten die meisten seiner schlimmsten Bugs.

Die Ankündigung listet eine Auswahl dessen auf, was sie allein in v1.3.14 behoben haben: ein heap-use-after-free in node:zlib, ein use-after-free in node:http2 durch re-entrante Callbacks, Out-of-Bounds-Schreibvorgänge in UDPSocket.sendMany, ein Speicherleck pro tls.connect, ein double-free im CSS-Parser. Die Liste geht weiter, und sie ist technisch.

Sumner macht unmissverständlich klar, dass das nicht Zigs Schuld ist. „We wouldn’t have gotten this far if not for Zig, and I’ll always be grateful.“ Das Problem ist strukturell: Lifetimes über eine GC-Grenze zu handhaben, ist in jeder Sprache schwer, die nicht dafür entworfen ist, und Zigs Antwort — explizites defer an jeder Aufrufstelle — lässt sich in selten erreichten Fehlerpfaden leicht falsch machen.

Er erwog C++. Etwa 20 % von Bun sind schön C++, und mehr davon würde Konstruktoren und Destruktoren bringen. Aber es würde weiter auf Style-Guides setzen, die per Code-Review durchgesetzt werden, und Speicherkorruption würde trotzdem passieren.

Rusts Versprechen ist anders: In safe Rust sind use-after-free, double-free und „Freigeben vergessen“ Compilerfehler. Der Borrow Checker und Drop machen aus einem Style-Guide-Problem ein Typensystem-Problem.

Die Strategie: transpilieren, nicht neu entwerfen

Umschreibungen haben einen schlechten Ruf, und zwar zu Recht. Eine Neuentwicklung von 535K Zeilen hätte Features und Fixes für ein Jahr eingefroren. Buns gewählte Form war ein mechanischer Port: dieselbe Architektur, dieselben Performance-Ziele, dieselben Features, dieselbe Testsuite — nur in Rust.

Zwei Entscheidungen trieben die ganze Anstrengung:

  1. Alles auf einmal, nicht inkrementell. Summers Erfahrung beim Port von esbuild nach Zig sagte ihm, dass inkrementelle Umschreibungen temporäres Gerüst hinterlassen, das mehr schadet als hilft.
  2. Lass es transpiliert aussehen. Das Ziel war Rust, das sich wie das Zig liest, aus dem es kam, um es erst nach v1.4 Richtung idiomatischem Rust zu refaktorieren.

Entscheidend: Buns Testsuite ist in TypeScript geschrieben, also ist es ihr egal, in welcher Sprache die Runtime implementiert ist. Das erlaubte ihnen, „alle Tests grün“ als Definition von fertig zu nehmen.

Die Vorbereitung war klein, aber bewusst. Bevor er Code schrieb, verbrachte Sumner ~3 Stunden mit Claude, um Zig-Muster auf Rust abzubilden; dieses Gespräch wurde zu PORTING.md (es tauchte später auf Hacker News auf). Ein zweiter Durchlauf analysierte die korrekten Rust-Lifetimes jedes Struct-Felds über den gesamten Codebase und serialisierte sie in LIFETIMES.tsv. Beide Dokumente gingen durch eine adversariale Überprüfung, bevor eine einzige Zeile portiert wurde.

Was an Ort und Stelle blieb: JavaScriptCore und die eingebetteten C/C++-Bibliotheken. Umgeschrieben wurde der native Kleber und der Bun-spezifische Code, nicht die JS-Engine.

Ausführung: 64 Claudes, 11 Tage

Der Port lief als etwa 50 „dynamic workflows“ in Claude Code, durchgehend, für 11 Tage. Jeder Workflow war eine Schleife: eine Aufgabe nehmen, Code erzeugen, reviewen lassen, Feedback anwenden.

Das Review-Modell ist der interessante Teil. Pro Implementierer gab es zwei oder mehr adversariale Reviewer — separate Claude-Sessions, denen gesagt wurde, sie sollten annehmen, der Code sei falsch, und jeden Grund finden, warum er scheitert. Der Implementierer reviewed nie; der Reviewer implementiert nie. Die Ankündigung stellt das als Spiegelbild menschlichen Reviews dar: Der Autor will mergen, also prüft eine andere Partei.

Ein paar Mechaniken lohnen sich herauszugreifen:

  • Erst Probe laufen lassen. Sie portierten 3 Dateien, bevor sie sich auf alle 1.448 festlegten.
  • Worktree-Sharding. Frühe Durchläufe ließen Claudes sich gegenseitig mit git stash und git reset auf die Füße treten. Die Lösung: 4 Worktrees, je 16 Claudes, mit der Regel, mitten in einer Aufgabe nie git oder cargo zu starten.
  • Compiler-Fehler als Arbeitswarteschlange. cargo check spuckte ~16.000 Fehler in eine Datei, nach Crate gruppiert, und 64 Claudes arbeiteten sie ab — 16 Fix/Review/Apply-Schleifen über 4 Worktrees.
  • Zyklische Abhängigkeiten. Zig war effektiv eine einzige Compile-Einheit; Rust brauchte ~100 Crates. Das Entwirren der Zyklen brachte die meisten dieser 16.000 Fehler ans Licht.
  • Isolation. Stresstests, die TCP-Sockets erschöpfen oder ~10k Prozesse spawnen, liefen unter systemd-run-cgroups. Die Maschine ging trotzdem zweimal leer auf der Platte und crashte.
timeline
    title Buns 11-Tage Zig → Rust-Umschreibung (Mai 2026)
    2026-05-03 : Port-Branch geöffnet (PR #30412)
    2026-05-04 : Erster Entwurfs-Batch mit 100 Dateien
    2026-05-06 : ~16.000 Compiler-Fehler, 64 Claudes
    2026-05-08 : Erster CI-Durchlauf (972 fehlschlagende Dateien)
    2026-05-09 : Linux x64 wird grün
    2026-05-11 : Windows grün (letzte Plattform)
    2026-05-14 : Alle 6 Plattformen grün, gemergt

In Zahlen: Spitze von 1.300 Codezeilen pro Minute, 695 Commits in der geschäftigsten Stunde (6. Mai), 58 Commits in der geschäftigsten Minute. Jeder Commit wurde von zwei adversarialen Reviewern geprüft, bevor er landete. Der finale Diff war +1.009.272 Zeilen. Kein einziger Test wurde übersprungen oder gelöscht.

CI ist der Punkt, an dem es real wurde. Zwei Tage nach dem ersten Durchlauf fielen die fehlschlagenden Testdateien von 972 auf 23. Eineinhalb Tage später war Linux komplett grün. Windows beendete als Letztes am 11. Mai; alle sechs Plattformen waren im Build #54202 am 14. Mai grün.

Was Rust tatsächlich gebracht hat

Das erklärte Ziel war Stabilität, und der Post stützt das mit harten Zahlen.

Bugs. Bun v1.4.0 behebt 128 Bugs, die in v1.3.14 reproduzierbar sind.

Speicher. Drop ersetzte das defer pro Aufrufstelle. Das Beispiel im Post: dasselbe 60-Module-Projekt 2.000 Mal in einem Prozess bündeln. In v1.3.14 leckt jeder Build ~3 MB für immer; in v1.4.0 läuft der Speicher flach aus.

Builds v1.3.14 v1.4.0
500 1.914 MB 526 MB
1.000 3.506 MB 586 MB
1.500 5.097 MB 608 MB
2.000 6.745 MB 609 MB

Ein früherer Versuch, das in Zig zu fixen, wurde nie gemergt, schreibt Sumner, weil das Fehlen eines Drop-Äquivalents das Zutrauen erschwerte.

Binärgröße. Allein die Rust-Umschreibung schnitt 3,8 MB (Windows), 5,5 MB (macOS), 6,8 MB (Linux) ab — vor allem durch das Wegfallen von übermäßigem Zig-comptime. Weitere Linker-Arbeit (Identical Code Folding, Kürzen der ICU-Daten) brachte die Gesamtschrumpfung auf ~20 % unter Linux und Windows.

Version Plattform Größe
v1.4.0 Windows 76 MB
v1.3.14 Windows 94 MB
v1.4.0 Linux 70 MB
v1.3.14 Linux 88 MB

Geschwindigkeit. Cross-Language-LTO zwischen C/C++ und Rust lässt den Compiler über Sprachgrenzen hinweg inline. Bun maß Gewinne von 2–5 %:

  • Bun.serve: 169,6k → 177,7k req/s (+4,8 %)
  • express: 64,5k → 66,6k (+3,2 %)
  • next build: 13,62s → 13,03s (+4,5 %)
  • tsc -b --force: 0,94s → 0,89s (+4,7 %)

Prisma verschob seine öffentliche Compute-Beta auf die Rust-Umschreibung. Alexey Orlenko: „We ran into memory leaks and a connection pool that couldn’t recover after a VM was paused and resumed. When the Rust rewrite appeared, we tested it against the same failure modes. It handled them perfectly.“

Claude Code v2.1.181 (17. Juni) und neuer nutzen den Rust-Port. Der Start ist unter Linux 10 % schneller. Summers Urteil zur vom Nutzer sichtbaren Änderung: „Boring is good.“

Wo der Port strauchelte

Ein getreuer Transpile von 535K Zeilen ist nicht umsonst. Der Post dokumentiert 19 bekannte Regressionen, alle behoben. Die lehrreichen sind kleine semantische Lücken zwischen den Sprachen:

  • debug_assert!-Nebenwirkungen. Zigs assert ist eine Funktion, ihr Argument läuft also in jedem Build. Rusts debug_assert! ist ein Makro, das im Release gelöscht wird — ein insert_stale-Aufruf, der HMR-State mutierte, hörte still auf zu laufen. (#30678)
  • Slices mit ungerader Länge. Zigs Helper ignorierte ein nachgestelltes ungerades Byte; bytemuck::cast_slice crasht darauf. Blob.text() auf einem UTF-16-BOM plus ungerader Byte-Anzahl begann abzustürzen. (#31188)
  • Bounds-Checks. Zigs ReleaseFast entfernt sie; Rust behält sie. Eine Platzhalter-Konstante senkte die Dateinamen-Interning-Obergrenze von 8,4 Mio. auf 270K, und echte Projekte stießen dagegen. (#31503)
  • comptime-Formatstrings. Zig wertet Formatstrings zur Compile-Zeit aus, daher sind Farbmarker weg, bevor Argumente eingesetzt werden. Rust hat kein comptime, also fraß der Marker-Parser einen literalen Backslash in OSC-8-Hyperlinks. (#30693)

Das sind die Bugs, die nur auftauchen, weil zwei Sprachen identisch aussehen, es aber nicht sind.

Was das bedeutet

Nimmt man den LLM-Winkel weg, bleibt eine echte Engineering-Geschichte: Ein Projekt stieß an die Decke dessen, was manuelle Speicherverwaltung in großem Maßstab bringen konnte, und wählte Rusts Borrow Checker als Werkzeug, um eine ganze Klasse von Bugs unmöglich statt nur unerwünscht zu machen.

Der LLM-Winkel ist der Teil, über den gestritten wird. Sumner ist offen, dass das drei Ingenieure mit vollem Codebase-Kontext etwa ein Jahr gekostet hätte — und dass sie es nie getan hätten; die realistische Alternative war „nichts tun und die Bugs weiterfixen“. Stattdessen machte ein Ingenieur, der 64 Claudes beaufsichtigte, es in 11 Tagen für ~165K $.

Etwa 4 % von Buns Rust-Code stehen in unsafe-Blöcken (~13.000 unsafe-Keywords auf ~27.000 Zeilen von ~780.000 insgesamt), und 78 % dieser Blöcke sind eine einzelne Zeile. Seit dem Merge liefen 11 Runden Security-Review, und sie haben 24/7 Coverage-guided Fuzzing über alle Parser gelegt — 100 Milliarden Ausführungen bisher, ~15 PRs.

Die Frage der Wartbarkeit ist die, die ich im Auge behalten würde. Sumner sagt, das Rust lese sich wie das Zig, und zeigt einen Side-by-Side von canMergeSymbols, um den Punkt zu machen. Die Wette ist, dass ein getreuer Port portweise reviewbar ist — was es einer Person erlaubte, einen Millionen-Zeilen-Diff abzusegnen.

Seine Schlusszeile ist die, die interessant altern wird: „One engineer can do a lot more today than a year ago.“

Referenzen

Diese Seite teilen