Bun in Rust neu schreiben
(bun.com)- Bun, das mit Zig begann, ist zu einer Runtime mit über 22 Millionen Downloads pro Monat herangewachsen; wiederholte Stabilitätsprobleme durch das Zusammenspiel einer GC-basierten JavaScript-Engine mit manueller Speicherverwaltung wurden jedoch zum Auslöser für den Wechsel zu Rust
- Statt 535.496 Zeilen Zig-Code ein Jahr lang manuell zu migrieren, liefen in Claude Code etwa 50 dynamische Workflows und bis zu 64 Claude-Instanzen 11 Tage lang parallel
- Das Porting wurde mit
PORTING.md,LIFETIMES.tsv, einem Implementierer und mindestens zwei adversarialen Reviewern sowie der bestehenden TypeScript-Testsuite validiert und bestand auf CI für 6 Plattformen zu 100 % - Nach dem Wechsel zu Rust behebt Bun v1.4.0 128 Bugs, die sich in v1.3.14 reproduzieren lassen, beseitigt alle instrumentierbaren Memory Leaks und reduziert die Binärgröße unter Linux und Windows um rund 20 %
- Bun v1.3.14 ist die letzte Zig-Version, v1.4.0 die erste Rust-Version und wird als Canary bereitgestellt; das Team nutzt Borrow Checker, Miri, LeakSanitizer und 24/7 coverage-basiertes Fuzzing als Werkzeuge zur Verbesserung der Stabilität
Bun begann mit Zig und hatte Stabilitätsprobleme
- Bun startete als Projekt, das den JavaScript-/TypeScript-Transpiler von esbuild aus Go zeilenweise portierte
- Der erste Zig-Code wurde am 16. April 2021 geschrieben, und Zigs Low-Level-Kontrolle sowie performanceorientiertes Design ermöglichten die frühe Implementierung von Bun
- Das frühe Bun wurde ein Jahr lang von einer einzelnen Person in Zig entwickelt und hatte einen sehr großen Umfang
- JavaScript-, TypeScript- und CSS-Transpiler, -Minifier und -Bundler
- npm-kompatibler Paketmanager
- Jest-ähnlicher Test Runner
- Node.js- und TypeScript-kompatible Modulauflösung
- HTTP/1.1- und WebSocket-Clients
- Implementierungen von Node.js-APIs wie
fs,netundtls
- Heute wird die Bun-CLI über 22 Millionen Mal pro Monat heruntergeladen, Claude Code und OpenCode verwenden sie als Runtime, und Vercel, Railway und DigitalOcean bieten 1st-party-Support
Wiederholte Bugs bei der Speichersicherheit
- Beispiele für in Bun v1.3.14 behobene Bugs umfassen use-after-free, double-free, Memory Leaks, out-of-bounds-Zugriffe und Race Conditions
- heap-use-after-free durch Aufruf von
.reset()während eines asynchronen.write()innode:zlib - use-after-free in
node:http2, bei dem ein reentranter JS-Callback ein Rehashing der Hashmap auslöst und dadurch interne Stream-Pointer ungültig werden - Problem in
UDPSocket.send()undsendMany(), bei dem Callbacks vonvalueOf()odertoString()einArrayBufferdetachen - Crashes und out-of-bounds-Reads in
Buffer#copyundBuffer#fill, wenn während der Argument-Coercion einArrayBufferdetached oder resized wird - Memory Leaks im Zusammenhang mit
crypto.scrypt,tlsSocket.setSession()undfs.watch() - double-free bei der Verarbeitung von Vendor Prefixes und Multi-Layer-Backgrounds im CSS-Parser
- Crash durch Race Condition bei
MessageEventwährend gleichzeitiger Zugriffe aufBroadcastChanneloderMessagePort
- heap-use-after-free durch Aufruf von
- Bereits zuvor wurden verschiedene Maßnahmen zur Verbesserung der Stabilität eingesetzt
- Unterstützung für Address Sanitizer in den Zig-Compiler gepatcht und die ASAN-Testsuite bei jedem Commit ausgeführt
- Unter Windows Auslieferung von Zig-Safety-checked-ReleaseSafe-Builds
- 24/7-Fuzzing der Bun-Runtime-API mit Fuzzilli
- Betrieb zahlreicher End-to-End-Tests auf Memory Leaks
- Die Position ist nicht, dass Zig selbst das Problem sei; die Anforderung, GC-Werte und manuell verwalteten Speicher gemeinsam zu handhaben, war die Hauptquelle der Stabilitätsprobleme
Warum Rust gewählt wurde
- JavaScript ist eine GC-Sprache, und Engines wie JavaScriptCore und V8 haben strenge Regeln für Exception Handling und Garbage Collection
- Zig verwaltet Speicher wie C nicht automatisch, hat keine Konstruktoren oder Destruktoren, und Cleanup muss meist an jedem Call Site explizit mit
deferangegeben werden - Bei Bun war die korrekte Behandlung der Lifetimes von GC-Werten und manuell verwalteten Werten eine der größten Ursachen für Stabilitätsprobleme
- Es muss geprüft werden, wo allokierte Bytes wieder freigegeben werden
- Es muss garantiert werden, dass nur einmal freigegeben wird
- JavaScript-Exception-Handling muss korrekt geprüft werden
- Es muss sichergestellt werden, dass GC-Pointer für den conservative stack scanner sichtbar sind
- Zigs Cleanup-Mechanismen sind explizite
deferunderrdefer; C++ nutzt Destruktoren und Move-Semantik, Rust verwendetDrop - Im bestehenden Zig-Code von Bun gab es eine Mischung aus Arena-Lifetimes, Reference Counting und sorgfältigem Review
- Ownership-Regeln lassen sich zwar mit Styleguide und Code-Review erzwingen, aber in sicherem Rust werden use-after-free, double-free und fehlende
free-Aufrufe auf Error Paths zu Compilerfehlern - Etwa 20 % des Bun-Codes sind C++, zudem werden mehrere C-/C++-Bibliotheken eingebettet
- JavaScriptCore
- uWebSockets und usockets
- lshpack und lsquic
- BoringSSL
- SQLite
- Auch C++ wäre eine Option gewesen, würde aber weiterhin auf Styleguides und Code-Review angewiesen bleiben, und selbst mit ASAN können Speicherbeschädigungen und Leaks auftreten
Strategie für das Rewrite: auf einmal und mechanisch
- Der bestehende Zig-Code von Bun umfasste ohne Kommentare 535.496 Zeilen, und ein traditionelles Rewrite wurde als Arbeit für ein kleines Engineering-Team über etwa ein Jahr eingeschätzt
- Da Bugfixes, Sicherheitsupdates und Feature-Entwicklung nicht für ein Jahr gestoppt werden konnten, wurde ein mechanisches Porting mit minimalen Änderungen am Nutzerverhalten als risikoärmster Ansatz gewählt
- Die Testsuite von Bun war in TypeScript geschrieben und hing nicht von der Implementierungssprache der Runtime ab
- Ein inkrementelles Rewrite wurde als kurzfristig und mittelfristig schmerzhaft bewertet, weil dafür temporärer Code nötig wäre, der später wieder gelöscht werden müsste; deshalb wurde alles auf einmal migriert
- Der Rust-Code wurde so geschrieben, dass er wie aus dem Zig-Code transpiliert wirkt; nach Bun v1.4 soll dann schrittweise
unsafereduziert und in idiomatisches Rust refaktoriert werden
Dynamische Workflows in Claude Code
- Für das Rust-Rewrite liefen in Claude Code etwa 50 dynamische Workflows über 11 Tage hinweg durchgehend
- Die Workflows reichten vom Schreiben eines Porting-Guides über Dateikonvertierung, das Beheben von Compilerfehlern, das Wiederherstellen von Subcommands, das Bestehen der vollständigen Testsuite bis hin zu groß angelegtem Cleanup
- Erstellen eines Porting-Guides, der Zig-Muster und -Typen auf Rust-Muster und -Typen abbildet
- Mechanisches Portieren aller
.zig-Dateien zu.rs-Dateien gemäßPORTING.mdundLIFETIMES.tsv - Beheben von Compilerfehlern pro Crate
- Wiederherstellen des Verhaltens von Subcommands wie
bun testundbun build - Bestehen der vollständigen Testsuite
- Groß angelegte Refaktorierung und Cleanup
- Während des Großteils der Zeit las ein Mensch die Workflow-Ausgaben, prüfte Probleme und Bugs und passte die Prompts an, damit Claude die Schleifen korrigiert
- Als Vorarbeit wurde etwa drei Stunden lang mit Claude besprochen, wie sich Muster aus der Zig-Codebasis auf Rust abbilden lassen; das Ergebnis wurde in
PORTING.mdserialisiert - Um dem manuell verwalteten Speicher-Code Rust-Lifetimes hinzuzufügen, wurde ein Workflow ausgeführt, der die Lifetimes aller Struct-Felder analysiert
- Auffinden von Feldern mit komplexen Lifetimes
- Vorschlagen von Lifetimes
- Review durch zwei adversariale Review-Agents
- Übernahme des Feedbacks und Speicherung in
LIFETIMES.tsv
Adversariales Review-Verfahren
- Für jede Implementierung wurde in einem separaten Context Window von Claude ein adversarialer Reviewer Claude eingesetzt; der Reviewer erhielt nur den Diff und wurde angewiesen, davon auszugehen, dass der Code falsch ist, und Bugs zu finden.
- Die Grundstruktur bestand aus 1 Implementierer, mindestens 2 adversarialen Reviewern und 1 Fixer.
- Die Bugs, die die Reviewer tatsächlich fanden, kompilierten zwar alle, hatten aber Probleme im Laufzeitverhalten.
uv_closeist asynchron, aberBox<uv::Pipe>wurde am Ende des Match-Arms gedroppt, wodurch libuv auf bereits freigegebenen Speicher zeigte; das führte zu Use-after-free und Double-free.- Bei negativer, nicht ganzzahliger File Time erzeugte die Verwendung von
trunc()einen negativennsec, eintimespec-Fehler. unwrap_orwertet sein Argument eager aus, wodurch im Fall einer ausgelassenen Percentage beicolor-mix()ein Panic ausgelöst wurde.
- Wie bei menschlichen Reviews wurden auch hier der Context von Autor und Reviewer getrennt, um Bias zu verringern, der daraus entstehen kann, dass der Implementierer mergen möchte.
Durchführung des großangelegten Portings und Parallelisierung
- Bevor alle 1.448
.zig-Dateien portiert wurden, wurde das Verfahren zunächst mit 3 Dateien validiert.- 1 Implementierer schrieb die
.rs-Datei. - 2 Reviewer prüften, ob sie mit
.zigund dem Verhalten übereinstimmt undPORTING.mdsowieLIFETIMES.tsveinhält. - 1 Fixer übernahm die Vorschläge.
- 1 Implementierer schrieb die
- Zu Beginn des vollständigen File-Portings führten mehrere Claude-Instanzen
git stash,git stash popundgit reset HEAD --hardaus und gerieten dadurch gegenseitig in Konflikt. - Danach wurden dem Workflow Regeln hinzugefügt, die
git stash,git reset,git-Befehle außer dem Committen bestimmter Dateien sowie langsame Befehle wiecargoverboten. - Am Ende wurden 4 Workflow-Shards und 4 Worktrees verwendet, und in jedem Shard commiteten und pushten 16 Claude-Instanzen Dateien.
- Dank Parallelisierung und Vorbereitung schrieb Claude in der Spitze etwa 1.300 Zeilen Code pro Minute.
- Der Port-Branch hatte 6.502 Commits ohne Merge-Commits, die Peak-Stunde lag bei 695 Commits, und der final gelandete Diff umfasste +1.009.272 Zeilen.
- Da die Standard-IOPS der EC2-Instanz nicht erhöht worden waren, konnte schon ein langsames
grepdazu führen, dass Disk-Reads und -Writes für mehrere Minuten stillstanden.
Compilerfehler und Aufteilung in Crates
- Nachdem der gesamte Code geschrieben war, behebt der Claude-Workflow Compilerfehler.
- Die Zig-Codebasis war faktisch eine einzige Compilation Unit, während der Rust-Code für schnellere Builds in etwa 100 Crates aufgeteilt werden sollte.
- Die schwierigste Fehlerkategorie waren zyklische Abhängigkeiten.
- Allein der PR zur Crate-Aufteilung direkt vor dem Rust-Rewrite reichte nicht aus.
- Ein separater Workflow klassifizierte und dokumentierte, wo Code mit zyklischen Abhängigkeiten untergebracht werden sollte.
- Ein weiterer Workflow führte das entsprechende Refactoring durch.
- Nach dem Auflösen der zyklischen Abhängigkeiten wurden rund 16.000 Compilerfehler sichtbar.
- Diese Fehler wurden pro Crate parallel bearbeitet.
- In jeder Crate wurde
cargo checkausgeführt. - Die Ausgabe wurde nach Datei gruppiert und gespeichert.
- Die Compilerfehler der jeweiligen Crate wurden behoben.
- 2 adversariale Reviewer prüften die Änderungen.
- 1 Fixer übernahm die Korrekturen.
- In jeder Crate wurde
- Es gab auch einen Fehlstart, bei dem Claude „alle Crates kompilierbar machen“ als Aufforderung verstand, Function Stubs zu erzeugen.
- Als sich ein Muster entwickelte, Workarounds mit langen erklärenden Kommentaren zu rechtfertigen, wurde als Review-Regel ergänzt: „Wenn ein absatzlanger Kommentar nötig ist, ist der Code falsch und der Code muss repariert werden.“
Der Weg bis zum Bestehen der Tests
- Nachdem
cargo checkdurchlief, wurden der Reihe nach Linkerfehler, Panics direkt nach dem Start,bun --versionund die Ausführung vonbun test <file>behoben. - Für Fehlschläge nach CLI-Subcommand wurden Stacktraces in Dateien gespeichert und mit einem Workflow aus Implementierer-, Reviewer- und Fixer-Schleife korrigiert.
- Der Testdatei-Workflow shardete etwa 100 zufällige Testdateien auf 4 Worktrees, speicherte Stacktraces und Fehler pro Fehlschlag und behob sie dann.
- Die Test-Suite enthielt Memory-Leak-Tests und Integrationstests, die in Debug-Builds in Timeouts laufen konnten.
- Ein Test, der
next devausführt und prüft, ob Hot Module Reloading 100 Änderungen erkennt - Ein Stresstest, der die maximale Zahl an TCP-Sockets ausschöpft
- Ein Test für Disk-Reads und -Writes im Gigabyte-Bereich
- Ein Test, der rund 10.000 Prozesse spawnt
- Ein Test, der
- Zur Isolation wurden mit
systemd-runund cgroups Speicher- und CPU-Nutzung begrenzt und der PID-Namespace getrennt. - Trotzdem stürzte die Maschine mehrfach wegen fehlendem Speicherplatz auf der Festplatte ab.
- Zwei Tage nach dem ersten CI-Lauf war die Zahl der fehlschlagenden Testdateien von 972 auf 23 gesunken, und anderthalb Tage später war Linux vollständig grün.
- Am Ende liefen die kompletten CI-Tests auf 6 Plattformen durch.
- macOS x64
- macOS arm64
- Linux x64
- Linux arm64
- Windows x64
- Windows arm64
- Nach 100 % bestandenen Tests prüften Menschen manuell, dass die Tests tatsächlich ausgeführt und nicht übersprungen wurden, und führten dann den Merge durch.
- Der Merge nach
mainwar kein versioniertes Release; es bestand noch nicht genug Vertrauen für ein Release, aber genug Vertrauen, um sich vollständig auf das Rewrite zu konzentrieren.
Testumfang und Kosten
- In 11 Tagen, vom 3. Mai bis zum Merge am 14. Mai, entstanden 6.778 Commits.
- Es wurden keine Tests gelöscht oder übersprungen.
- Der Testumfang pro Plattform war wie folgt.
- Debian 13 x64:
expect()1.386.826-mal, 60.624 Tests, 4.174 Dateien - macOS 14 arm64:
expect()1.259.953-mal, 58.850 Tests, 4.175 Dateien - Windows 2019 x64:
expect()1.007.544-mal, 57.337 Tests, 4.173 Dateien
- Debian 13 x64:
- Für die Arbeiten vor dem Merge wurden 5,9 Milliarden uncached Input Tokens, 690 Millionen Output Tokens und 72 Milliarden gelesene cached Input Tokens verbraucht.
- Zu API-Listenpreisen lagen die Kosten bei rund 165.000 US-Dollar.
- Die Einschätzung lautet, dass 3 Ingenieure mit Context über die gesamte Codebasis von Hand ungefähr 1 Jahr gebraucht hätten.
- Als verwendetes Modell wird das Pre-Release-Claude Fable 5 genannt, und Bun enthält die Disclosure, dass es im Dezember 2025 von Anthropic übernommen wurde.
Security-Review, Fuzzing und Stand von unsafe
- Nach dem Merge des Rust-Ports wurden mit Claude Code Security 11 Runden Security-Review abgeschlossen und die Findings bearbeitet.
- Für alle Parser von Bun wurde 24/7 Coverage-guided Fuzzing hinzugefügt.
- JavaScript
- TypeScript
- JSX
- CSS
- JSON5
- JSONC
- TOML
- YAML
- Markdown
- INI
- Bun-Shell-Skripte
- semver-Bereiche
.patch-Dateien- CSS-Farben
- Der Fuzzer sendet gefundene Bugs an Claude, damit dieser reproduzierbare PRs inklusive Fix einreicht; Menschen reviewen dann den PR.
- Bislang wurden die Parser 100 Milliarden Mal ausgeführt, was zu etwa 15 PRs führte.
- Zum Zeitpunkt des Schreibens befinden sich etwa 4 % des Rust-Codes innerhalb von
unsafe-Blöcken.- etwa 13.000
unsafe-Keywords - etwa 27.000 Zeilen / insgesamt rund 780.000 Zeilen
- 78 % der
unsafe-Blöcke sind einzeilig und betreffen Pointer aus C++ oder Aufrufe von C-Bibliotheken
- etwa 13.000
- Da weiterhin C/C++-Bibliotheken wie JavaScriptCore verwendet werden, werde es immer mehr
unsafegeben als in einem reinen Rust-Projekt.
Nach der Rust-Umstellung entdeckte Regressionen
- Das Rust-Rewrite war eine große Änderung und verursachte 19 bekannte Regressionen, die inzwischen alle behoben wurden.
- Die meisten entstanden durch Code, dessen Syntax in beiden Sprachen ähnlich ist, dessen Bedeutung sich aber unterscheidet.
-
Side Effects in
debug_assert!- Zigs
assertist eine Funktion, daher werden die Argumente in allen Builds ausgeführt. - Rusts
debug_assert!ist ein Makro, daher wird im Release-Build der gesamte Ausdruck entfernt. - Der Aufruf von
insert_staleverschwand im Release-Build, wodurch ein bestimmter HMR-Fall in HTML-Route-Projekten mit React kaputtging. - Zugehöriges Issue: #30678
- Zigs
-
Slice mit ungerader Länge
- Buns Zig-Helper
reinterpretSlice(u16, bytes)verwendete@divTruncund ignorierte dadurch das nachlaufende ungerade Byte. - Rusts
bytemuck::cast_sliceführt bei ungerader Länge zu einem Panic. - Es gab eine Regression, bei der
Blob.text()mit einem UTF-16-BOM und einem ungeraden Byte danach keinen String zurückgab, sondern den Prozess per Panic beendete. - Der Fix bestand darin, das ungerade Byte mit
&buf[..buf.len() & !1]wieder zu ignorieren. - Zugehöriges Issue: #31188
- Buns Zig-Helper
-
Bounds Checks
- Der Zig-Code auf macOS und Linux wurde mit
ReleaseFastkompiliert, wodurch Bounds Checks entfernt wurden; Rust-Release-Builds behalten Bounds Checks bei. - Die Größe des Overflow-Blocks im Bun-Module-Resolver blieb beim Platzhalter
64, wodurch die Obergrenze von 8,4 Millionen interned filenames auf 270.272 sank. - Das portierte Off-by-one bei
ptrs[4095]wurde dadurch in realen Projekten erreichbar, und Rust löste statt eines Out-of-Bounds-Write einen Panic aus. - Zugehöriges Issue: #31503
- Der Zig-Code auf macOS und Linux wurde mit
-
comptime-Format-Strings- In Zig ist bei
Output.prettydasfmtcomptime, daher werden Farbmarker wie<r>und<d>vor der Argumentersetzung in ANSI-Escapes umgewandelt. - Die Rust-Funktion hat keinen
comptime-Parameter, verarbeitete die Marker daher im fertigen String und schrieb dabei auch die Argumente fälschlich um. - Bei
bun update -ikollidierten die OSC-8-Hyperlink-Terminierung und der nachgestellte<r>-Marker, sodassrals Text ausgegeben wurde. - In Rust war dafür das Makro
bun_core::pretty!("<r>{}<r>", hyperlink)nötig. - Zugehöriges Issue: #30693
- In Zig ist bei
Behobene Bugs und Memory Leaks
- Bun v1.4.0 behebt 128 Bugs, die sich in v1.3.14 reproduzieren ließen.
- Das Spektrum reicht von Memory Leaks über Crashes bis hin zu falsch eingefärbtem Hilfetext.
- Rusts
Dropruft automatisch die Funktiondropauf, wenn ein Wert den Scope verlässt. - In Zig musste an jeder Call Site
deferhinzugefügt werden, wodurch leicht Cleanup fehlte oder doppelt ausgeführt wurde. Dropist die Entscheidung, versteckten Control Flow zu akzeptieren, um dafür häufige Footguns zu reduzieren.Dropbehebt mehrere Memory Leaks rund um Dateipfade im Error-Handling-Code.- Buns Integration mit LeakSanitizer wurde verbessert und verfolgt jetzt alle native code memory allocations.
- Alle instrumentierbaren Memory Leaks wurden behoben.
-
Verbesserungen bei Leaks in
Bun.build()- In Bun v1.3.14 überlebten bei jedem In-Process-Aufruf von
Bun.build()der geparste Source-Text und die AST-Symboltabelle die Lebensdauer des Builds und verursachten jeweils Leaks von mehreren MB. - In einem Test, der dasselbe Projekt mit 60 Modulen 2.000-mal in einem einzigen Prozess bundelte, leakte v1.3.14 pro Build weiterhin etwa 3 MB.
- In Bun v1.4.0 flacht der Speicherverbrauch ab.
- | Builds | Bun v1.3.14 | Bun 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 |
- In Bun v1.3.14 überlebten bei jedem In-Process-Aufruf von
Binärgröße, Stack-Nutzung, Performance
- Allein die ersten Änderungen des Rust-Rewrite verkleinerten bereits die Binärgröße.
- Windows: 3,8 MB weniger
- macOS: 5,5 MB weniger
- Linux: 6,8 MB weniger
- Der Hauptgrund war, dass im Zig-Code
comptimezu häufig verwendet wurde. - Danach kamen außerdem Identical Code Folding, das Entfernen ungenutzter Daten aus ICU und das verzögerte Dekomprimieren von Teilen von libicu mit einem zstd dictionary hinzu.
- Zusammengenommen reduzieren Rust-Rewrite, ICU-Änderungen und Identical Code Folding die Größe des Bun-Binaries unter Linux und Windows um etwa 20 %.
| Version | Plattform | Größe |
|---|---|---|
| Bun v1.4.0 canary | Windows | 76 MB |
| Bun v1.3.14 | Windows | 94 MB |
| Bun v1.4.0 canary | Linux | 70 MB |
| Bun v1.3.14 | Linux | 88 MB |
- Der TOML-Parser und Buns Recursive-Descent-Parser benötigen nun weniger Stack-Speicher.
- Rusts LLVM-IR-Codegen gibt für Stack-Variablen die Intrinsics
llvm.lifetime.startundllvm.lifetime.endaus, sodass LLVM Stack-Slots wiederverwenden kann. - Zuvor wurden besonders große Funktionen manuell in mehrere kleinere Funktionen refaktoriert, um ein offenes Issue in Zig zu umgehen.
- Rust unterstützt Cross-Language Link-Time Optimization zwischen C/C++ und Rust, wodurch sprachübergreifendes Inlining möglich wird.
-
Linux-x64-Benchmarks
- Bun v1.3.14 und Bun v1.4.0 wurden auf Linux x64 auf einem EC2 Xeon Platinum 8488C verglichen.
- Der HTTP-Durchsatz wurde mit oha, die App-Workloads mit hyperfine gemessen.
- | server | Bun v1.3.14 | Bun v1.4.0 | Δ |
- | --- | ---: | ---: | ---: |
- | Bun.serve | 169.6k req/s | 177.7k req/s | +4.8% |
- | node:http | 103.8k req/s | 108.5k req/s | +4.5% |
- | Elysia | 158.9k req/s | 163.3k req/s | +2.8% |
- | express | 64.5k req/s | 66.6k req/s | +3.2% |
- | fastify | 91.5k req/s | 95.9k req/s | +4.8% |
- | workload | Bun v1.3.14 | Bun v1.4.0 | Δ |
- | --- | ---: | ---: | ---: |
- | next build | 13.62 s | 13.03 s | +4.5% |
- | vite build | 1.69 s | 1.65 s | +2.2% |
- |
tsc -b --force| 0.94 s | 0.89 s | +4.7% |
Praxisbeispiele und Release-Status
- Prisma hat die Public Beta von Prisma Compute auf Basis von Buns Rust-Rewrite veröffentlicht
- Laut Prisma wurden im Rust-Rewrite ein Connection Pool, der sich nach VM-Pause/Resume nicht erholt, sowie ein Memory-Leak-Failure-Mode getestet, und diese Failure Modes seien gut behandelt worden
- Claude Code v2.1.181 und Versionen seit dem Release vom 17. Juni verwenden den auf Rust portierten Bun
- Der Start von Claude Code unter Linux wurde um 10 % beschleunigt, ansonsten hätten die meisten Nutzer den Unterschied kaum bemerkt
- Bun v1.3.14 ist die letzte in Zig geschriebene Bun-Version
- Bun v1.4.0 ist die erste in Rust geschriebene Bun-Version und wird als Canary bereitgestellt
Werkzeuge, die das Team gewonnen hat, und verbleibende Arbeit
- Die neue Rust-Codebasis behält eine Form bei, die der bestehenden Zig-Codebasis sehr ähnlich ist
- Sie wurde so geschrieben, dass Personen, die den ursprünglichen Zig-Code verstehen, auch den mechanisch übersetzten Rust-Code verstehen können
- Das Review der Rust-Rewrite-PRs erfolgte so, dass ein adversarial Review-Agent prüfte, ob Inkonsistenzen zwischen Zig und Rust sowie die Einhaltung des Porting Guide und des Lifetime Guide korrekt erkannt werden, während Menschen viel Code Side-by-Side lasen
- Bun v1.4 macht Bun schneller und kleiner, senkt den Speicherverbrauch und liefert Werkzeuge für Verbesserungen bei der Stabilität
- Rust Borrow Checker
- Miri
- LeakSanitizer
- 24/7 coverage-guided Fuzzing für den Parser
- Es gibt noch Teile, die refaktoriert werden müssen; bun-unsafe-audit ist verlinkt
- Ein einzelner Engineer überwachte Fable und Claude Code genau und erreichte in nur 11 Tagen den Zustand, dass die gesamte Test-Suite auf allen Plattformen erfolgreich durchläuft
1 Kommentare
Lobste.rs-Kommentare
extern "C"-Wrapper-Code löschen können, wäre aber weiterhin auf einen per Code-Review erzwungenen Styleguide angewiesen gewesen, und selbst mit ASAN hätte es weiter Speicherkorruption und Speicherlecks gegeben.Lustigerweise läuft Node.js auch mit C++ gut, aber ich habe Bun nie als ernsthaftes Projekt betrachtet. Inzwischen wirkt es eher wie eine Testbench für die Marketingabteilung von Anthropic, daher werde ich weiter Abstand davon halten.
Spoiler: In der Praxis sind die Leute nicht so vorsichtig, und Fehler passieren trotzdem.
unsafe-Blöcken, und 78 % davon sind einzeilig“ klingt wie ein Versuch zu beruhigen, aber ob einunsafe-Block nur eine Zeile hat, ist nicht wichtig. Wenn darin die Sicherheitsgarantien verletzt werden, kann dadurch potenziell die Soundness des gesamten Codes außerhalb des Blocks mit beschädigt werden.Im frühen Merge des Rust-Ports von Bun war genau so eine offensichtliche Unsoundness enthalten: https://github.com/oven-sh/bun/issues/30719
Auf das Issue haben die Maintainer reagiert, indem sie Rusts Miri-Tool in der CI aktiviert haben, und da im Abschnitt „What's Next“ des Artikels auch Miri (which runs for a growing chunk of code in CI) steht, scheint man in diese Richtung weiterzuarbeiten, was gut aussieht.
Fairerweise kann selbst Rust mit Sicherheitsverletzungen trotzdem wartbarer sein als der ersetzte Zig-Code, je nachdem, wie dessen Qualität war. Aber die Anzahl der Codezeilen pro unsafe-Block ist kein Qualitätsmaßstab, insbesondere dann nicht, wenn es in diesen Blöcken auch sonst an anderen Coding-Praktiken, Expertise oder automatisierten Prüfungen fehlte.
unsafe-Blöcke nicht durch den Portierungsprozess entstanden sind, sondern aus den Projektanforderungen resultieren. Wenn man C-Bibliotheken aufruft, braucht manunsafe-Blöcke, und die lassen sich nicht einfach durch Refactoring entfernen.Natürlich ginge das, wenn man auch diese C-Bibliotheken neu schreiben würde, aber das könnte man gegebenenfalls später prüfen.
In der Ankündigung „Bun is joining Anthropic“ sagte Jarred, dass für die Arbeit an Bun mehr Ingenieure eingestellt würden, aber wenn man sich nur GitHub ansieht, scheint das Bun-Team eher kleiner geworden zu sein. Ich weiß nicht genau, welche Schlussfolgerung man daraus ziehen sollte, außer vielleicht: „Bun ist ein kleines Team.“
Die Methode selbst ist interessant, aber der Artikel liest sich wie ein Marketingstück. Es fehlt an einer Analyse, wie hoch die Kosten tatsächlich waren, die Risiken eines Rust-Rewrites werden nicht behandelt, und auch die konkrete Erklärung, warum es überhaupt zu dem Rewrite kam, bleibt schwach. Meine Vermutung wäre, dass es am no ai policy von Zig liegt oder dass es intern bei Anthropic eine Strategie gab, sich stärker auf Rust zu konzentrieren.
Und warum der Rewrite stattfand, dazu erzählt der Artikel meines Erachtens eine ziemlich konsistente Geschichte. Bun hatte Maßnahmen ergriffen, um die Probleme in den Griff zu bekommen, stürzte aber weiterhin ab, und die Entwickler wollten eine systematischere Methode, um solche Probleme zu verhindern.
Der ursprüngliche Plan war offenbar, einen bestimmten Coding-Stil strenger durchzusetzen und Smart Pointer einzuführen, aber Jared hielt eigene Smart Pointer für schlechter benutzbar als Rust und ohne vergleichbare Garantien. Daraus wurde dann wohl: „Was wäre, wenn wir eine Woche lang testen, ob das neue Modell von Anthropic Bun nach Rust umschreiben kann?“ Als dann immer mehr Tests bestanden, wechselte die Einschätzung von „könnte machbar sein“ zu „wir mergen das“.
Es wirkt also weniger so, als habe man sich von Anfang an für einen Rust-Rewrite entschieden, sondern eher wie: „Rust scheint die Lösung für das Problem zu sein, aber wegen der Kosten eines Rewrites geht das nicht. Doch mit einer LLM-Portierung sieht es plötzlich machbar aus. Dann machen wir eben den LLM-Rewrite.“
Glückwunsch an Jarred, das Bun-Team und Anthropic, dass sie das geschafft haben.