6 Punkte von GN⁺ 2 시간 전 | Noch keine Kommentare. | Auf WhatsApp teilen
  • Entwickler bei Anthropic haben im vergangenen Monat mit Claude Fable 5, Claude Opus 4.8 und dynamischen Workflows 10 Pakete mit Codebasen im Umfang von zehntausenden bis hunderttausenden Zeilen migriert und dabei nicht einzelne Codezeilen repariert, sondern den iterativen Prozess zur Code-Generierung verbessert
  • Die Zig→Rust-Migration von Bun erzeugte in weniger als zwei Wochen 1 Million Zeilen und bestand vor dem Merge 100 % der bestehenden Tests; ein Python→TypeScript-Projekt migrierte an einem Wochenende 165.000 Zeilen und nutzte Hunderte Agenten, 8 Stufen-Gates und 3 adversarielle Reviews
  • Groß angelegte Migrationen lassen sich parallelisieren, der bestehende Code dient als Spezifikation und Referenzlösung, und fehlgeschlagene Kompilierungen oder Tests erzeugen automatisch die nächste Aufgabenliste, wodurch sich gut ein objektiver Validierungs-Loop aufbauen lässt
  • Der Ablauf erfolgt schrittweise: von der Vorbereitung der Bewertungsmaßstäbe über Rulebook, Abhängigkeitskarte und Gap-Inventar bis zu Stress-Tests der Regeln, vollständiger Übersetzung, Kompilierung, Ausführung und Verhaltensvergleich; wiederkehrende Fehler werden nicht dateiweise behoben, sondern durch Anpassen übergeordneter Regeln und erneute Generierung
  • Die Kosten liegen weiterhin bei zehntausenden bis hunderttausenden Dollar oder mehr, doch fehlgeschlagene Branches können verworfen und neu gestartet werden; die Bun-Migration kostete zu API-Preisen rund 165.000 Dollar und erreichte danach geringere Speichernutzung, 19 % kleinere Binärdateien und 2–5 % bessere Leistung bei realen Workloads

Den Generierungs-Loop statt des Codes verbessern

  • AI-Code-Migration bedeutet, dass Agenten eine Produktions-Codebasis in eine neue Sprache oder ein neues Framework übertragen
    • Statt Dateien direkt zu übersetzen, schreiben Engineers Migrationsregeln und Validierungs-Loops
    • Die Agenten wiederholen Übersetzen, Kompilieren und Testen, bis das Verhalten des neuen Codes dem Original entspricht
    • Projekte, die früher viele Jahre dauerten, können so auf wenige Wochen schrumpfen
  • Bei Anthropic wurden mit Claude Fable 5, Claude Opus 4.8 und dynamischen Workflows innerhalb eines Monats 10 Codepakete mit zehntausenden bis hunderttausenden Zeilen migriert
  • Das zentrale operative Prinzip besteht darin, nicht den generierten Code direkt nachzubessern, sondern den Loop zu verändern, der diesen Code erzeugt hat

Konkrete Migrationsbeispiele

  • Buns Zig→Rust-Migration

    • Jarred Sumner hat mit Claude Code Bun von Zig nach Rust migriert
    • In weniger als zwei Wochen wurden 1 Million Zeilen Code erzeugt
    • Vor dem Merge bestand der Code in der CI 100 % der bestehenden Bun-Test-Suite
    • Die 19 nach dem Merge entdeckten Regressionen wurden alle behoben
    • Der Rust-Port wurde im Juni in Claude Code übernommen
    • Bun verzeichnet monatlich mehr als 10 Millionen Downloads und wird auch intern bei Claude Code breit genutzt
  • Python→TypeScript-Migration

    • Mike Krieger migrierte an einem Wochenende eine Python-Codebasis in 165.000 Zeilen TypeScript
    • Dabei kamen Hunderte Agenten, 8 Stufen-Gates und 3 adversarielle Reviews zum Einsatz
    • Eine abschließende Äquivalenzprüfung verglich die Ausgabe aller Befehle mit dem Python-Original
    • Das gesamte Migrationsergebnis wurde mehrfach verworfen, Regeln und Workflow wurden angepasst, und das Resultat des dritten Durchlaufs wurde übernommen

Wann sich eine Sprachmigration erneut lohnt

  • Eine Migration kann sinnvoll werden, wenn sich das technische Umfeld seit der Erstentwicklung verändert hat, frühere Kompromisse nun zu Einschränkungen werden, bessere Ansätze entstanden sind oder das ursprüngliche Ökosystem geschrumpft ist
  • Zig bot C-nahe Performance und Einfachheit und passte damit gut zur frühen Phase, in der Bun von einer Person entwickelt wurde, doch diese Einfachheit brachte bekannte Trade-offs mit sich
  • Früher musste man für eine Sprachmigration die Roadmap unterbrechen und Ressourcen über mehrere Quartale bereitstellen
    • Zwei Codebasen mussten womöglich über mehrere Quartale oder Jahre parallel gepflegt werden
    • Wenn die endgültige Verhaltensgleichheit nur 90 % erreichte, konnte danach ein noch größeres Wartungsproblem entstehen als vorher
    • Heute gibt es die Option, einen gescheiterten Branch zu löschen und den Lauf neu zu starten
  • Eine Migration von 1 Million Zeilen erfordert nicht mehr zwangsläufig 3 bis 4 Millionen Dollar Engineering-Kosten über vier Jahre, kann aber weiterhin zehntausende bis hunderttausende Dollar oder mehr kosten
    • Die Bun-Migration verbrauchte 5,9 Milliarden uncachete Eingabe-Token und 690 Millionen Ausgabe-Token
    • Zu API-Preisen entspricht das Kosten von rund 165.000 Dollar
    • Bei Mikes Port verbrauchte der Kernabschnitt 27 Millionen Token
  • Die Business-Begründung für eine Migration muss nicht mehr existenzentscheidend sein; bereits ein Jahr wiederholter Memory-Bugs oder ein chronischer Bottleneck kann reichen
  • Python-Build-Bottleneck beseitigen

    • Mikes internes Tool wurde als einzelne Binärdatei ausgeliefert, doch der Python-Toolchain brauchte etwa 8 Minuten, um plattformspezifische Binärdateien zu erzeugen
    • Über die gesamte Build-Matrix hinweg bedeutete das pro Release rund 30 Minuten Wartezeit
    • Nach der TypeScript-Migration sank die Kompilierzeit auf etwa 2 Sekunden, der Binärstart wurde 6-mal schneller und eine separate Deployment-Pipeline konnte entfallen

Warum AI-Agenten gut für Migrationen geeignet sind

  • Parallele Arbeit ist möglich
    • Die Arbeit kann in Tausende unabhängige Einheiten wie Dateien oder Crates zerlegt und gleichzeitig von vielen Agenten bearbeitet werden
  • Der bestehende Code dient als klare und umfassende Spezifikation
    • Er lässt sich auch als zentrale Referenz für Anweisungen an Übersetzungsagenten nutzen
  • Die Test-Suite fungiert als eingebauter Bewertungsmechanismus
    • Wenn die Validierung objektiv ist, kann das Modell über Tage hinweg iterieren, ohne dass Menschen die Qualität ständig vermitteln müssen
  • Fehlgeschlagene Kompilierungen oder Tests werden automatisch zu den nächsten Arbeitspaketen, wodurch der Bedarf an einer separaten Aufgabenliste sinkt
  • Konsistenz und Ausnahmebehandlung lassen sich in den Loop einbauen
    • Reviewer verknüpfen jedes Problem mit einer verletzten Regel
    • Die Lösung für eine Ausnahme wird anschließend zu einer Regel, der alle Agenten folgen
    • Statt stiller Verhaltensabweichungen werden Regelverstöße zu expliziten Aufgaben
  • Fable und Opus 4.8 werden genutzt, um parallele Arbeit von Subagenten zu delegieren, zu steuern und zu validieren sowie mehrere Wege zum Ziel zu finden
  • Mit einem Advisor-Pattern, das verschiedene Modellklassen kombiniert, wird der Token-Verbrauch optimiert

Voraussetzung: Original und Port gleichwertig bewerten

  • Vor Beginn der Migration braucht es einen starken Bewertungsmechanismus, der Quell- und Zielcode nach denselben Maßstäben prüft
    • Ohne ihn gibt es weder Erfolgskriterium noch Abbruchbedingung
    • Tests, die von internen Funktionen der Ausgangssprache abhängen, lassen sich im Zielcode eventuell nicht unverändert ausführen
  • Bestehende Tests werden aufgeteilt in solche, die sich als externe Aufrufe ausdrücken lassen, und solche, die an interne Implementierungsdetails gebunden sind und nicht portiert werden
  • Tests des externen Verhaltens werden als Assertions neu formuliert, die sowohl auf dem Original als auch auf dem Port laufen
    • Ein adversarieller Agent überprüft, dass diese Assertions bei der Umschreibung nicht abgeschwächt wurden
  • Der Bewertungsmechanismus wird zunächst auf dem Originalcode ausgeführt; anschließend wird geprüft, ob er bei absichtlich beschädigtem Code fehlschlägt
  • Jarred verfügte bereits über eine große Test-Suite in einer dritten Sprache, TypeScript
  • Mike baute mit 7 realen Nutzungsszenarien ein Äquivalenz-Harness und behandelte jede Verhaltensänderung als zu behebenden Bug

Phase 1: Rulebook, Abhängigkeitskarte und Gap-Inventar

  • Die grundlegenden Artefakte sind nicht bloß Übersetzungsergebnisse, sondern eine Liste der Stellen für Refactoring, ein Rulebook für die Übersetzungsweise und eine Abhängigkeitskarte zur Festlegung der Arbeitsreihenfolge
  • Die Reihenfolge der Erstellung ist wichtig
    • Zuerst müssen die Defaults im Rulebook festgelegt werden, damit sich Punkte, die davon nicht abgedeckt sind, als Gap-Inventar definieren lassen
    • Rulebook und Gap-Inventar werden gemeinsam auditiert und validiert
  • Rulebook

    • Die Form des Rulebooks hängt davon ab, ob der neue Code die bestehende Struktur beibehält oder vollständig neu gestaltet wird
    • Wenn die Struktur wie bei Jarred erhalten bleibt, steht eine Zuordnungstabelle für Typen und Idiome zwischen den Sprachen im Mittelpunkt; schwer übersetzbare Konstrukte verweisen auf das Gap-Inventar
    • Bei einer Neugestaltung wie bei Mike fungiert das Rulebook als Designdokument
    • Jarred entwickelte im Dialog mit Claude für jeden unklaren Bereich Richtlinien und richtete 8 Subagenten ein, die jeweils eine von 8 erwarteten Fehlerkategorien überprüften
  • Abhängigkeitskarte

    • Für parallele Migrationen müssen Dateiabhängigkeiten erfasst werden, um zu entscheiden, welche Dateien zuerst und welche im selben Batch migriert werden
    • In Legacy-Code ohne explizites Manifest und in Codebasen etwa in C/C++ oder Python müssen Abhängigkeiten eigenständig entdeckt und kartiert werden
    • Claude-Code-Agenten können die Karte erzeugen, indem sie deterministische Skripte schreiben, ausführen, prüfen und anpassen
    • Ein generalisiertes Beispiel gibt es im Prompt für die Abhängigkeitskarte
  • Gap-Inventar zwischen Sprachen und skeptische Reviewer

    • Das Gap-Inventar dokumentiert Wissen, das im bestehenden Code implizit enthalten ist, in der Zielsprache aber explizit gemacht werden muss
    • Bei Zig→Rust war das Modell der Speicherverwaltung der zentrale Unterschied
    • In Zig kann nur im Kommentar stehen, dass der Aufrufer einen Buffer freigeben muss; vergisst man das, kompiliert der Code trotzdem, und Leaks fallen erst zur Laufzeit auf
    • In Rust geht Ownership an den Aufrufer über, Speicher wird automatisch freigegeben, und Use-after-move oder doppeltes Freigeben kompiliert nicht
    • Bei Python→TypeScript waren Interfaces und Verträge der zentrale Unterschied
    • Python erfordert keine Deklaration der Form übergebener Objekte oder Rückgabewerte
    • In TypeScript müssen Verträge für Methoden, Argumente und Rückgabeformen geschrieben werden, damit der Code kompiliert
    • Jarred listete die Unterschiede vor der Übersetzung auf, Mike erst danach im Audit; je nach Projekt sind also beide Ansätze möglich
    • Ein generalisiertes Beispiel gibt es im Prompt zur Erstellung des Gap-Inventars

Phase 2: Stress-Test der Regeln

  • Vor der vollständigen Migration wird das Rulebook mit einer kleinen Testmigration durchgeschüttelt, um Probleme früh zu finden
  • Jarred verglich drei Agentenläufe
    • Der erste Agent übersetzte nach dem Rulebook 3 Dateien
    • Der zweite Agent übersetzte denselben Umfang wie ein erfahrener Rust-Engineer
    • Der dritte Agent formulierte aus den Unterschieden beider Ergebnisse neue Übersetzungsregeln
  • So wurden zwei gravierende Probleme gefunden, bevor sie sich auf alle 1.448 Dateien ausbreiten konnten
  • Dieser Ansatz funktioniert nur bei strukturtreuen Migrationen, bei denen sich zwei Übersetzungen derselben Datei zeilenweise vergleichen lassen
  • Bei einer Neugestaltung wie bei Mike greifen adversarielle Reviewer das Designdokument an, und die Validierung erfolgt über verwerfbare End-to-End-Läufe
  • Alle in dieser Testphase übersetzten Dateien sollten verworfen werden; das Ziel ist nicht schrittweiser Code-Fortschritt, sondern die Verbesserung der Regeln
  • Ein generalisiertes Aufgabenbeispiel gibt es im Prompt für den Stress-Test

Phase 3: Vollständige Code-Übersetzung

  • Alle folgenden Phasen nutzen denselben Multi-Agenten-Loop aus Implementieren → Reviewen → Korrigieren
  • Die Massenimplementierung kann kleinen Modellen überlassen werden, während große Modelle das Review übernehmen
    • Mike setzte für die parallele Kernmigration 12 Claude-Sonnet-Subagenten ein
  • Die Aufgabenliste wird mechanisch verwaltet
    • Ein Batch-Skript erkennt den Abschlussstatus über das Vorhandensein übersetzter Dateien auf der Platte
    • Verbleibende Dateien werden in Batches für Implementierungsagenten aufgeteilt
    • Da bei jedem Lauf die Queue aus dem Zustand auf der Platte rekonstruiert wird, ist Unterbrechen und Fortsetzen im Grunde eingebaut
  • Wenn Agenten zu vorsichtig nur kleine Mengen bearbeiten, kann man direkter anweisen und darauf verweisen, dass der Compiler im nächsten Schritt Fehler ohnehin findet
  • Unsichere Stellen werden mit // TODO(port): <reason> markiert und in Phase 4 gelöst
  • Die nachfolgenden Aufgaben entstehen automatisch aus Compilerfehlern, Smoke-Test-Abstürzen und Testfehlschlägen
  • Adversarielles Review und Regel-Updates

    • Zwei adversarielle Reviewer in getrennten Kontexten bewerten die Implementierung; bei Uneinigkeit entscheidet ein dritter Agent
    • Wenn sich derselbe Fehler über mehrere Dateien hinweg wiederholt, wird er nicht einzeln pro Datei behoben
    • Stattdessen wird dem Rulebook ein Satz hinzugefügt und der betroffene Batch neu erzeugt
    • Auch während der Übersetzungsphase wächst das Rulebook weiter; Code, der den Regeln widerspricht, wird nicht manuell gepatcht
  • Wo der Compiler eingebunden wird

    • Bei kurzen Compile-Zeiten kann er Teil des Übersetzungs-Loops sein
    • Mike ließ die TypeScript-Kompilierung in jedem Loop laufen, weil sie pro Einheit in Sekunden fertig war
    • Dauert das Kompilieren länger, wird es in die nächste Phase verschoben
    • Jarred verbot den Compiler im Übersetzungs-Loop, weil cargo mehrere Minuten brauchte
    • Ab hier werden die Prompts kürzer; Beispiele finden sich im Prompt zum Start der Übersetzung

Phase 4–6: Kompilieren, Ausführen, Verhaltensgleichheit

  • Die drei Phasen teilen sich dieselbe Loop-Struktur; je weiter der Prozess fortschreitet, desto weniger menschliches Urteil ist nötig
  • Je nach Sprache und Projektgröße kann die Kompilierphase in die vollständige Übersetzungsphase integriert werden
  • Phase 4: Kompilieren

    • Jarred richtete ein Orchestrator-Skript so ein, dass der Compiler einmal über den gesamten Workspace läuft
    • Fix-Agenten bearbeiten die Fehlerliste parallel, durchlaufen adversarielle Reviews, und anschließend wird erneut gebaut
    • Das Review der Fehlerliste dient nicht der Einzelbehebung, sondern dem Erkennen systemischer Probleme
    • Nachdem zirkuläre Imports korrigiert wurden, die durch Zig-Lazy-Compilation möglich waren, entstanden Tausende Rust-Modulfehler
    • Der Loop wurde um Logik ergänzt, die einordnet, welche Abhängigkeiten gelöscht oder verschoben und welche Grenzen neu zugeschnitten werden müssen
  • Phase 5: Ausführung und Smoke-Tests

    • Abstürze in Smoke-Tests fungieren ebenso als mechanische Ground Truth wie Compilerfehlerlisten
    • Probleme werden nicht einzeln abgearbeitet, sondern nach Ursachen gruppiert und dann von adversariellen Subagenten geprüft
  • Phase 6: Vergleich mit dem Originalverhalten

    • Der übersetzte, kompilierte und per Smoke-Test geprüfte Code wird aufgeteilt, und die vorbereitete Test-Suite wird sowohl auf Original als auch Port ausgeführt
    • Fix-Agenten prüfen fehlgeschlagene Tests zusammen mit beiden Codebasen; adversarielle Reviewer kontrollieren die Korrekturen
    • Nur der Build-Daemon darf Binärdateien neu bauen
    • Fix-Agenten erstellen Patches, der Daemon bündelt sie und baut nur einmal neu
    • Anschließend werden die betroffenen Tests erneut ausgeführt und die Ergebnisse zurückgegeben
    • So wird die Arbeit serialisiert, damit nicht mehrere Agenten jeweils teure Builds ausführen
    • Wiederholt sich derselbe Fehler über viele Tests hinweg, wird die übergeordnete Regel korrigiert, die den Bug erzeugt hat, und nur die dadurch betroffenen Dateien werden neu generiert
  • Wenn es keine Test-Suite gibt

    • Mike ließ Claude ein kleines Skript erzeugen, das 7 reale Szenarien sowohl auf dem ursprünglichen Python-Code als auch auf dem neuen Port ausführt und die Ergebnisse vergleicht
    • Für jedes fehlgeschlagene Szenario wurde ein eigener Fix-Agent zugewiesen, und der Loop lief weiter, bis alle 7 bestanden
    • Claude entwarf außerdem eine eigene End-to-End-Test-Suite und ließ sie über Nacht autonom laufen
    • Das Muster aus Fehler beheben und erneut ausführen wiederholte sich über vier Nächte
    • Die vorab definierte Szenarienliste half sogar dabei, schwer vorhersehbare kleine Usability-Probleme aufzudecken
    • Auch ohne bestehende Tests kann Claude den Originalcode als Referenz nutzen, um einen Bewertungsmechanismus zu bauen

Operative Prinzipien aus wiederholten Durchläufen

  • Statt einem Leitfaden blind zu folgen, sollte man zunächst gemeinsam mit Claude einen Migrationsplan passend zu den Projekteigenschaften erstellen
  • Einzelne Fehlschläge sollten Fix-Agenten überlassen werden; Menschen sollten sich auf wiederkehrende Fehlermuster konzentrieren
  • Reviews sollten adversariell, Validierung mechanisch aufgebaut sein
    • Adversarielles Review ist bei lang laufender Arbeit hilfreich und kann den zusätzlichen Token-Verbrauch wert sein
    • Skripte wie Compiler, diff und Test-Suite sollten als finale Richter dienen
  • Nicht für jede Aufgabe wird das größte Modell verwendet
    • Kleine Modelle eignen sich zur Parallelisierung von Massenimplementierung
    • Die größten Modelle sollten sich auf Reviews und das Formulieren der Regeln konzentrieren, denen andere Agenten folgen
  • Menschliche Arbeitszeit sollte frühzeitig in Rulebook und Stress-Tests investiert werden; danach besteht der Prozess meist aus dem Abarbeiten der Queue
  • Der Abschlussstatus sollte mechanisch erkennbar sein, etwa durch „Ausgabedatei existiert auf der Platte“, und die Queue muss fortsetzbar sein

Ergebnisse und Grenzen der Bun-Migration

  • Der Rust-Port von Bun läuft in Produktion, doch rund 4 % des Rust-Codes liegen in unsafe-Blöcken
    • Meist handelt es sich um einzeilige Pointer-Operationen an C/C++-Grenzen
  • Alle mit Tools erkennbaren Memory-Leaks wurden behoben
    • In einem Benchmark mit 2.000 wiederholten Builds sank der Speicherverbrauch von 6.745 MB auf 609 MB
  • Die Größe der Linux- und Windows-Binärdateien sank um 19 %
  • Durch sprachübergreifende Optimierungen verbesserte sich die Performance realer Workloads wie HTTP-Services, next build und tsc um 2–5 %
  • Bei großen Migrationen sollte man weniger jede einzelne generierte Zeile prüfen als vielmehr die vom Loop erzeugten Ergebnisse und Wiederholungsmuster

Verwandte Materialien

Noch keine Kommentare.

Noch keine Kommentare.