Meine Gedanken zur Rust-Neuschreibung von Bun
(en.liujiacai.net)- Die Rust-Neuschreibung von Bun ist eher die Entscheidung, zum Mainstream gehörenden Technologie-Stack zu wechseln, während die bestehende Architektur und die Datenstrukturen aus Zig beibehalten werden
- Der Rewrite-PR umfasste 6.755 Commits und wurde nach der Öffnung am 8. Mai bereits am 14. Mai gemergt, was Fragen zur Tiefe des Reviews bei einer Änderung an einer produktionsreifen Runtime offenlässt
- Das Bestehen von Tests verifiziert nur bekannte Pfade; globale Invarianten wie Fehlerpfade, Nebenläufigkeit und extreme Speicherbedingungen lassen sich damit nur schwer garantieren
- Der Kern ist nicht ein Scheitern von Zig, sondern eine strukturelle Fehlanpassung zwischen der auf schnelle Releases ausgerichteten Teamkultur von Bun und den Kosten manueller Speicherverwaltung
- Die eigentliche Wette ist nicht Zig gegen Rust, sondern ob sich von KI erzeugter und nicht ausreichend reviewter Code langfristig warten lässt
Das von Zig geschaffene Fundament von Bun
- Bevor man sich den Rust-Rewrite-PR von Bun ansieht, sollte man festhalten, dass Zig einen großen Anteil daran hatte, Bun an seine heutige Position zu bringen
- Jarred wählte Zig nicht, weil es „cool“ war, sondern weil es einem kleinen Team ermöglichte, schnell einen performanten JS-Runtime-Prototyp ohne Garbage Collection zu bauen
- Die geringe Reibung in Zig, die direkte Speichermanipulation und die einfache C-Interoperabilität ermöglichten die frühe Performance von Bun und die kleine Teamgröße
- Da Jarred sagte, „die Architektur ändert sich nicht, und die Datenstrukturen ändern sich auch nicht“, ist die Rust-Neuschreibung eher eine Struktur, die das in Zig gebaute Skelett übernimmt
- Erst mit Zig das Fundament bauen, das Produkt launchen, Investments einsammeln und dann nach Übernahme und Wachstum des Unternehmens auf einen stärker etablierten Technologie-Stack wechseln, kann als normale Business-Entscheidung gelten
- Das jedoch als Beleg für eine generelle Untauglichkeit von Zig darzustellen, ist schwer haltbar
Das Risiko einer großen Neuschreibung, die in 6 Tagen gemergt wurde
- Der Rewrite-PR hinterließ die Eckdaten 6.755 Commits, Branch-Name
claude/phase-a-port, am 8. Mai geöffnet und am 14. Mai gemergt - Das Kernproblem ist, dass eine vollständige Neuschreibung einer produktionsreifen JS-Runtime in nur 6 Tagen gemergt wurde
- Ein Grundprinzip der Softwareentwicklung lautet: „Code, den man nicht versteht, sollte nicht in Produktion laufen.“
- Dieser Grundsatz gilt nicht unbedingt deshalb, weil solcher Code sicher fehlerhaft ist, sondern weil man im Fehlerfall nicht weiß, wo man mit der Analyse anfangen soll; damit wird er zur Untergrenze von Wartbarkeit
- In der Liste der Reviewer des PR standen
coderabbitai[bot]undclaude[bot], und der einzige menschliche Revieweraliihatte den Status „Awaiting requested review“ - Eine geschlossene Schleife, in der Claude schreibt und Claude reviewt, ist logisch nicht unmöglich, bedeutet aber faktisch, dass niemand den gesamten Codebestand tatsächlich von Anfang bis Ende gelesen hat
Was das Bestehen von Tests nicht garantieren kann
- Selbst wenn die Test-Suite auf allen Plattformen erfolgreich durchläuft, verifiziert sie nur die Korrektheit von bekanntem Verhalten und bekannten Pfaden
- Eine Test-Suite kann die folgenden Bereiche nur schwer ausreichend absichern
- ob Fehlerpfade korrekt behandelt werden
- wie sich das System an Randbedingungen unter Stress verhält
- ob die Konsistenz des Zustands in Nebenläufigkeitsszenarien erhalten bleibt
- ob das Speichermodell unter extremen Bedingungen der beabsichtigten Semantik entspricht
- Auch Jarred hat laut Zusammenfassung eingeräumt, dass Speicherprobleme beim erneuten Eintritt über die JS-Grenze vom Rust-Compiler nicht gelöst werden können und weiterhin auf Menschen angewiesen sind
- Das Problem ist, dass gerade dieser Teil, der menschliche Prüfung braucht, nicht von Menschen reviewt wurde
- Die Codeübersetzung durch KI kommt eher einer lokalen semantischen Äquivalenz nahe, bei der jede Funktion isoliert so angepasst wird, dass sie sich wie das Original verhält
- Globale Invarianten zwischen Funktionen sowie Designbeschränkungen, die nicht in Tests stehen und nur im Kopf des ursprünglichen Autors existieren, lassen sich allein durch KI-Übersetzung nur schwer absichern
- Solche Beschränkungen tauchen in aktuellen Tests womöglich nicht auf und zeigen sich erst 6 Monate später unter einer bestimmten Produktionslast als schwer erklärbarer Absturz
- Das ist nicht nur ein Problem von Claude, sondern gilt auch für alle Werkzeuge und menschlichen Programmierer, die ohne ausreichendes Review übersetzen
- Bei einem Umfang von 6.755 Commits wird dieses Risiko massiv verstärkt
Wer nach der Übernahme das Risiko trägt
- Das frühe Bun war ein Projekt, bei dem Jarred auf sich selbst setzte; der Einsatz von Zig, schnelle Iteration und das Akzeptieren technischer Schulden lassen sich als typische Startup-Logik verstehen, bei der man das Risiko selbst trägt
- Jetzt, da Bun von einem Großunternehmen übernommen wurde und eine Nutzerbasis hat, die es in echten Produktionssystemen einsetzt, hat sich der Risikoträger verschoben: nicht mehr Jarred, sondern die Engineers, die Bun in Produktion betreiben, und die Nutzer hinter ihnen
- Jarred sagt, diese Version sei noch canary, und vor dem offiziellen Release stünden noch Optimierungen und Aufräumarbeiten aus
- Canary ist eine Verteidigungslinie, aber kein Ersatz für menschliches Review
- Optimierung und Aufräumen sind Fragen der Codequalität; sie lösen nicht die Frage, ob die Maintainer den Code verstanden haben
- Ein Codebestand, den niemand im Team vollständig gelesen hat, bleibt für die Maintainer im Inneren eine Blackbox, selbst wenn die Tests umfassend sind oder die Canary-Phase lang dauert
- Dieses Problem kann sich später als ganz realer Schmerz zeigen, wenn vor Ort ein schwerer Bug debuggt werden muss
Der Fehler in der Diagnose des Zig-Problems
- Als frühere Begründung führte Jarred an, dass es in der Zig-Codebasis viele use-after-free, Double-Free und Speicherlecks in Fehlerpfaden gegeben habe
- Diese Diagnose an sich mag zutreffen, aber daraus lässt sich schwer der Schluss ziehen, dass „Zig nicht funktioniert“
- Präziser wäre die Diagnose, dass in einem kommerziellen Projekt mit Fokus auf schnelle Iteration die kognitiven Kosten manueller Speicherverwaltung das Budget des Teams überstiegen haben
- Das ist kein Bug von Zig, sondern eher eine strukturelle Fehlanpassung zwischen den Designzielen von Zig und dem Business-Modell von Bun
- Die Zielgruppe von Zig sind Systemprogrammierer, die wissen, was sie tun, und bereit sind, für maximale Kontrolle den Preis zu zahlen
- TigerBeetle hat mit Zig eine Datenbank geschrieben, die praktisch kaum Speicherfehler aufweist, was sich damit erklären lässt, dass Teamkultur und Projektcharakter zur Philosophie von Zig passten
- Die Teamkultur von Bun ist eher von schneller Iteration, schnellem Release und schnellem Bugfixing geprägt, und das steht in einem grundlegenden Spannungsverhältnis zu der strikten Speicherdiziplin, die Zig verlangt
- „Unser Team macht mit diesem Werkzeug häufig Fehler“ als „dieses Werkzeug ist ungeeignet“ zu interpretieren, kommt einem Attributionsfehler nahe
- Es wird die Analogie verwendet, dass ein Hammer nicht selbst schuld ist, nur weil er nicht zum Einsatzzweck passte
Kurzfristiger Ausblick und langfristiges Risiko
- Kurzfristig ist es gut möglich, dass die neu geschriebene Version überwiegend in Ordnung ist
- Die Hauptpfade sind durch Tests abgedeckt, offensichtliche Probleme werden in der Canary-Phase sichtbar, und die Garantien des Rust-Compilers eliminieren eine Art von Speicherfehlern
- Oberflächlich kann alles ganz normal wirken
- Langfristig bleiben jedoch 6.755 Commits, die kein Mensch vollständig gelesen hat, als strukturelles Risiko bestehen
- Wenn in 6 Monaten ein seltsamer Nebenläufigkeitsfehler auftritt oder unter bestimmter Last ein Randfall merkwürdiges Verhalten auslöst, steht der Debugging-Engineer vor einem System, das nie jemand wirklich verstanden hat
- Mit einem System, das nie verstanden wurde, ist nicht gemeint, dass es fehlerfrei ist, sondern dass niemand weiß, warum ein Fehler auftritt, wenn er einmal sichtbar wird
- Die eigentliche technische Wette dieser Neuschreibung ist nicht Zig gegen Rust, sondern ob sich von KI erzeugter und nicht reviewter Code in Produktionsumgebungen langfristig warten lässt
- Diese Frage ist komplexer als „alle Tests bestehen“ und reicht tiefer als „die Speichersicherheit von Rust“
- Das Fazit wird in die Metapher verdichtet: „Zig hat das Fundament gelegt, Claude hat das Gebäude errichtet, und der menschliche Reviewer ist noch unterwegs“
- Wie lange dieses Gebäude bewohnbar bleibt, hängt davon ab, ob beim ersten Leck jemand den Bauplan lesen kann
1 Kommentare
Lobste.rs-Kommentare
Bevor man darüber spricht, „Bun in Rust neu zu schreiben“, sollte man sagen, dass Bun Zig zu verdanken hat, wo es heute steht
Auch Bun-Entwickler Jarred sagt das so. Zig habe Bun möglich gemacht, und Zig habe einen großen Anteil daran, dass ein Projekt, an dem ein Jahr lang allein in einem stinkenden Zimmer in Oakland gearbeitet wurde, zu einem der am weitesten verbreiteten Werkzeuge im JavaScript-Ökosystem gewachsen ist
Außerdem sei Zig eine großartige Sprache, der Bun viel von seinem Erfolg verdanke, aber andere Projekte wie Ghostty oder Tigerbeetle hätten nicht die Stabilitätsprobleme gehabt, die Bun hatte
Normalerweise sind Datenbanken bei der Stabilität eine viel größere Herausforderung als Sprach-Runtimes
Dieser Text riecht stark nach einem von einem LLM geschriebenen Text
Daher könnte dieser Eindruck auch eine Spur von maschineller Übersetzung sein
Wenn die Zielgruppe von Zig „Systemprogrammierer sind, die wissen, was sie tun, und bereit sind, für ultimative Kontrolle den Preis zu zahlen“, dann wirkt das wie ein Wiederaufleben der C/C++-artigen Selbstüberschätzung im Kontext von Sprachen, die nach Rust und Swift erschienen sind
Wenn Jarred als Grund für den Wechsel angibt, dass es „zu viele Use-after-free-, Double-free- und Memory-Leaks auf Fehlerpfaden im Zig-Codebestand gab“, dann ist das ein Datenpunkt dafür, ob man eine speicherunsichere Sprache praktisch sicher machen kann, indem man ein LLM fleißig nach Memory-Safety-Bugs suchen lässt
Nicht einmal die LLM-Unternehmen selbst haben diesen Weg eingeschlagen
Die Aussage „Der Code wurde von Claude geschrieben und von Claude geprüft, also ist der geschlossene Kreislauf logisch nicht unmöglich, aber das bedeutet, dass kein Mensch diese gesamte Codebasis jemals wirklich gelesen hat“ scheint offensichtlich nicht zu stimmen
Wenn man eine von Menschen geschriebene Zig-Codebasis mechanisch nach Rust übersetzt hat, dann kann jemand, der den ursprünglichen Code versteht, auch den neuen Code verstehen. Es ist ja keine Portierung nach APL; beides sind prozedurale Sprachen in der Tradition der C-Syntax
Das Argument, dass Code sich künftig immer weiter vom menschlichen Verständnis entfernen könnte, wenn man LLMs ohne Aufsicht laufen lässt, bleibt offen, aber die Codebasis zum jetzigen Zeitpunkt scheint immer noch so verständlich, wie man es von einer millionenzeiligen JavaScript-Runtime und einem universellen Single-Binary-Tool erwarten kann
Der Satz „AI trifft nur lokale semantische Äquivalenz auf Funktionsebene und versteht keine globalen Invarianten zwischen Funktionen“ wirkt sowohl in einer LLM-freundlichen als auch in einer LLM-kritischen Lesart seltsam
LLMs sind nicht deterministisch genug, um zu garantieren, dass jede Funktion sich genauso verhält wie das Original. Für solche Garantien braucht man Werkzeuge wie
c2rust. Ein LLM kann Code übersetzen, der wie das Original aussieht, aber verhindern, dass((abc & 45) << 3) == 360zu((abc & 45) << 30) == 360wird, können nur der Compiler, die Testsuite und vielleicht die probabilistische Erkennung durch LLM-basiertes Code-ReviewUmgekehrt: Wenn es einen Übersetzer gäbe, der wie
c2rustdie Gleichheit jeder Funktion garantiert und zugleich wie ein LLM Kommentare und Struktur bewahrt, dann wäre das ein perfekter Übersetzer und könnte auch millionenzeilige Codebasen automatisch portieren. Compiler kann man ebenfalls als Sonderfall davon sehen, und Clang ist, auch wenn es nicht frei von Bugs ist, nah genug dran, dass Menschen ihm vertrauen. Wenn LLMs Zig oder C++ mit der Zuverlässigkeit von Clang nach Rust übersetzen könnten, wäre Chrome bis Ende dieses Monats in reinem RustAußerdem ist das Kodieren funktionsübergreifender Invarianten gerade der Kern eines Typsystems. Einer der Gründe, Bun in Rust neu zu schreiben, ist ja gerade, dass das Typsystem von Rust komplexe Invarianten besser ausdrücken kann. Anthropic hat auch nicht den gesamten Code in Assembler kompiliert und anschließend den Quelltext verbrannt
release fastvermutlich vollständig wegoptimiert :^)Bun wurde schon lange vor der Rust-Portierung vibe-coded. Man hat AI ziemlich früh übernommen, und seit der Übernahme durch Anthropic sind fast alle Commits von Bots geschrieben. Jarred hat auch getwittert, dass er am Wochenende AI Features schreiben ließ und so mehrere Funktionen hinzugefügt hat
HTML Parsers in Portland analysiert mehrere Portierungen eines Python-HTML-Parsers und zeigt, dass einer der Kernalgorithmen in jeder Portierung sehr unterschiedlich implementiert wurde. In allen Fällen wäre eine mechanische Portierung möglich gewesen, tatsächlich ist sie aber nicht so erfolgt
Natürlich ist das ein Beispiel von Anfang dieses Jahres und der Prozess war anders, aber einige Stücke, die ich nach der Portierung im Bun-Codebestand gesehen habe, scheinen ein ähnliches Problem zu haben
Aber dass „LLMs nicht deterministisch genug sind, um zu garantieren, dass jede Funktion sich wie das Original verhält“, führt letztlich zur Schlussfolgerung, dass kein Mensch die gesamte Codebasis jemals wirklich gelesen hat
Ein Beispiel für „alle Tests gehen durch“: https://github.com/oven-sh/bun/…
Dass auch in anderen Foren mehrere Leute denselben Commit verlinkt haben, legt nahe, dass man diesen Link irgendwo gesehen und das Gefühl gehabt hat, er passe zur eigenen Erwartung, ohne zu prüfen, ob er tatsächlich relevant ist. Ich finde, man sollte an sich selbst höhere Maßstäbe anlegen
Erster Commit: „await process exit / JSON-parse-retry instead of fixed sleeps“
Rücknahme: „test: revert proc.exited change in spawn.test.ts, keep isDebug iteration count“
Ich denke in letzter Zeit viel über das Prinzip nach: „Code, den man nicht versteht, sollte man nicht in Production ausführen“
Meine Karriere ist schon ziemlich weit fortgeschritten, aber ich habe mich bewusst gegen eine Managementlaufbahn entschieden, und auch mein Programmierweg ging immer weiter nach unten im Stack. Das liegt daran, dass ich es mag, das eigentliche Programm selbst tief zu verstehen. Features auszuliefern und das Leben der Nutzer zu verbessern, ist ebenfalls sehr wichtig, aber eine der großen inneren Belohnungen des Programmierens ist das Gefühl, wahre Aussagen über reale Systeme zu lernen
Für jemanden wie mich fühlt es sich fast axiomatisch an, dass Menschen jede Zeile eines Programms verstehen sollten. Mein Manager im Organigramm trägt jedoch Verantwortung für das Programm, das ich warte. Er ist ein großartiger Manager und micromanagt nicht, deshalb versteht er natürlich nicht jede Zeile der Software, die das Team ausliefert. Ehrlich gesagt weiß ich nicht einmal, ob er den Code überhaupt groß gelesen hat. Damit würde er dieses Prinzip ja ebenfalls verletzen
Ich frage mich, ob es einen bedeutenden Unterschied gibt zwischen „Code, den mein Mitarbeiter geschrieben hat und den ich nicht verstehe“ und „Code, den meine AI geschrieben hat und den ich nicht verstehe“
Mit 1 kann ich leben, bei 2 habe ich ein ungutes Gefühl. Ist der Unterschied nur, ob es einen Menschen gibt, der Verantwortung übernimmt? Ob es jemanden gibt, dem man die Schuld geben kann? Ich weiß noch nicht, ob das ausreicht, um das starke moralische Gefühl zu rechtfertigen, das ich dabei empfinde
Ich mache mir Sorgen, dass dieses starke Gefühl nicht essenziell für gutes Engineering ist, sondern eher eine persönliche Ästhetik. Eigene Vorlieben kann jeder haben, aber wenn es nur das ist, dann muss ich wohl damit rechnen, dass mir dieser Aspekt der Arbeit in Zukunft weniger Freude machen wird. Am Ende werde ich doch ins Management gedrängt, nur dass meine Mitarbeiter dann Bots sind
Es heißt, „Zig hat es kleinen Teams ermöglicht, ohne Garbage Collector und schwere Runtime schnell eine performante JS-Runtime zu prototypen“, aber die anderen großen Runtimes, Node und Deno, tun im Grunde dasselbe
Beide kapseln die JS-Engine in GC-freien Sprachen, nämlich C++ und Rust. Ob V8 oder JSC selbst tatsächlich zusammen mit einem Garbage Collector ausgeliefert werden, weiß ich nicht genau, aber das geht am Kern der Sache vorbei
Das gesamte Bun-Repository fühlt sich dystopisch an. Bots reden dort miteinander und erstellen unsinnige PRs
Beispiel: https://github.com/oven-sh/bun/issues/30766
Die Geschwindigkeit, mit der das als „fertig“ betrachtet wurde, ist ziemlich schockierend, aber ich sehe darin nicht den Zugunfall in Zeitlupe, als den der Text es ohne belastbare Belege darstellt
„Wenn in sechs Monaten seltsame Concurrency-Bugs auftauchen und ein Grenzfall unter bestimmter Last Fehlverhalten auslöst, wird der Engineer beim Debuggen auf ein System stoßen, das nie jemand wirklich verstanden hat“ ist reine Spekulation, und es werden keine Fakten dafür vorgelegt
Sprachportierungen sind ein Bereich, der gut zu LLMs passt, und der zugrunde liegende Code wurde von vielen Menschen verstanden. Ich sehe nicht, warum eine bloße Sprachportierung plötzlich die Diagnosefähigkeit irreversibel zerstören sollte