Eine Fabrik umzurüsten, während die Produktion läuft – so ähnlich fühlt es sich an, Code von einer Programmiersprache in eine andere zu übersetzen. Manuelles Vorgehen ist zeitaufwendig und fehleranfällig. Anthropic, das Unternehmen hinter dem KI-Assistenten Claude, setzt auf einen systematischen Prozess, der große Sprachmodelle mit Prüfmechanismen verbindet. Dieser Beitrag beschreibt, wie Anthropic solche Migrationen plant und umsetzt.
Der Ausgangspunkt jeder Migration ist nicht der erste Code, sondern ein verlässlicher Richter. Ohne ein klares Kriterium für den Erfolg tappt man im Dunkeln. Dieser Richter – „Judge“ genannt – muss alten und neuen Code nach denselben Maßstäben bewerten. Das klingt einfacher, als es ist: Testsuiten für die ursprüngliche Sprache greifen oft auf interne Funktionen zurück, die es in der Zielsprache nicht gibt. Anthropic löst das, indem es bestehende Tests kategorisiert: Welche lassen sich als externe Aufrufe formulieren? Welche hängen von Interna ab, die nicht portiert werden? Aus den brauchbaren Tests werden portable Assertions gebaut – Prüfungen, die gegen beide Versionen laufen. Und damit diese Prüfungen nicht schwächer werden, setzt man gegnerische KI-Agenten ein, die versuchen, die neuen Tests zu umgehen. Ein guter Judge besteht nicht nur den Originalcode, sondern fällt auch gezielt durch, wenn man manipulierten Code einschleust.
Ein konkretes Beispiel: Mike, ein Entwickler bei Anthropic, portierte einen Python-Code nach TypeScript. Da die vorhandenen Tests nicht einfach übertragbar waren, baute er ein kleines „Parity-Harness“ aus sieben realen Szenarien. Jede Abweichung im Verhalten galt als Bug. Das zeigt: Ein Judge muss nicht riesig sein, sondern treffsicher.
Der Sechs-Schritte-Plan: Vom Regelbuch zur finalen Prüfung
Anthropic hat den Prozess in sechs Schritte gegliedert, die auf zwei großen Migrationen basieren: Jarreds Portierung von Zig nach Rust (über 1.400 Dateien) und Mikes Python-zu-TypeScript-Übersetzung. Die Abläufe sind generalisiert, die Prinzipien gelten sprachübergreifend.
Schritt 1: Fundament legen – Regelbuch, Abhängigkeitskarte und Lückeninventar
Bevor eine Zeile Code geschrieben wird, entsteht ein Regelwerk. Es legt fest, wie die Übersetzung ablaufen soll. Es gibt zwei Szenarien: Entweder die neue Codebasis folgt derselben Struktur wie die alte (dann ist das Regelbuch eine Übersetzungstabelle für Typen und Idiome), oder sie wird komplett neu designt (dann ist es ein Architekturdokument). Jarred erstellte sein Regelbuch, indem er mit Claude diskutierte und für jede Unklarheit eine Policy formulierte. Acht spezialisierte Unteragenten prüften das Regelbuch auf typische Fehlermuster – basierend auf Jarreds eigener Erfahrung.
Parallel entsteht eine Abhängigkeitskarte. Denn wenn man mehrere Dateien parallel migriert, muss man wissen, welche Dateien zuerst kommen müssen. Für moderne Sprachen gibt es oft Manifestdateien, aber bei Legacy-Codebasen oder Sprachen wie C/C++ und Python müssen die Abhängigkeiten erst ermittelt werden. Claude Code kann hier Agenten losschicken, die ein deterministisches Skript schreiben und ausführen, um die Karte zu erstellen.
Zum Fundament gehört auch das Lückeninventar: Welche Konzepte der alten Sprache haben in der neuen keine direkte Entsprechung? Beispiel: Python verlangt keine Verträge über die Form von Objekten oder Rückgabewerten, TypeScript schon. Solche impliziten Annahmen müssen erfasst werden. Jarred inventarisierte die Lücken vor der Übersetzung, Mike übersetzte erst und auditierte dann. Beide Vorgehensweisen sind möglich, oft eine Kombination sinnvoll.
Schritt 2: Die Generalprobe – Regeln im Kleinen testen
Bevor der große Angriff startet, folgt ein Stresstest. Drei Dateien werden nach dem Regelbuch übersetzt, drei weitere von einem Agenten, der sich wie ein „Senior Rust Engineer“ verhält. Aus dem Unterschied zwischen beiden Versionen werden neue Übersetzungsregeln abgeleitet. Dieser Schritt deckte bei Jarreds Migration zwei kritische Probleme auf, die sonst tausendfach durchgeschlagen wären. Die erzeugten Dateien werden verworfen. Ziel ist es, die Regeln zu schärfen, nicht Fortschritt zu machen.
Für Design-getriebene Migrationen (wie Mikes) ist der Ersatztest eine konfrontative Prüfung des Entwurfsdokuments durch gegnerische Gutachter, gefolgt von einem komplett durchlaufenen E2E-Durchlauf, der ebenfalls verworfen wird.
Schritt 3: Die Übersetzungswelle
Jetzt geht es an die Massenfertigung. Eine Multi-Agenten-Schleife aus Implementierung, Review und Korrektur kommt zum Einsatz. Die Implementierer können kleinere, günstigere Modelle sein – Anthropic nutzte hier Claude Sonnet für zwölf Subagenten. Die Warteschlange ist mechanisch: Ein Batch-Skript prüft, ob die Zieldatei bereits auf der Festplatte existiert, und teilt die restlichen Dateien in Batches auf. Die Migration ist jederzeit fortsetzbar, auch nach einem Abbruch.
Ein Problem: Agenten neigen zur Übervorsicht. Die Lösung ist eine deutliche Anweisung: „Der Compiler fängt Fehler im nächsten Schritt – mach einfach.“ Alles, was der Übersetzer nicht sicher kann, markiert er mit // TODO(port): <Grund>. Diese offenen Punkte werden in Schritt 4 abgearbeitet. Zwei gegnerische Reviewer bewerten die Arbeit der Implementierer. Wenn ein Reviewer immer denselben Fehler sieht, wird nicht jede Datei einzeln geflickt, sondern das Regelbuch um einen Satz ergänzt und der betroffene Batch neu generiert. Der Code wird niemals von Hand gegen das Regelbuch korrigiert.
Ein wichtiger Design-Entscheid: Wo sitzt der Compiler? Mike ließ den TypeScript-Compiler in jeder Schleife laufen (weil er in Sekunden fertig ist), Jarred verbannte ihn aus der Schleife (weil cargo Minuten braucht) und verschob ihn in den nächsten Schritt.
Schritte 4–6: Kompilieren, Testen, Verhalten abgleichen
Diese drei Schritte teilen sich dieselbe Schleifenarchitektur und brauchen zunehmend weniger menschliches Eingreifen. Schritt 4 (Kompilieren) kann je nach Sprache und Umfang in Schritt 3 aufgehen. Bei Jarred rief ein Orchestrator-Skript den Compiler einmal über das gesamte Workspace auf. „Fixer Agents“ arbeiteten die Fehlerliste parallel ab, wieder mit gegnerischem Review. Dabei half es, systematische Fehler zu erkennen – zum Beispiel Tausende von Rust-Modul-Fehlern, die durch zyklische Importe entstanden, welche Zigs lazy compilation toleriert hatte. Der Fix war eine Logik im Loop, die entscheidet, welche Abhängigkeit gelöscht, verschoben oder neu strukturiert wird.
Schritt 5 (Ausführen) hat eine ähnliche mechanische Wahrheitsquelle: Abstürze aus dem Smoke-Test. Wieder werden die Fehler nach Ursache gruppiert und durch gegnerische Subagenten geprüft. Schritt 6 vergleicht das Verhalten beider Codebasen. Die übersetzten Dateien sind kompiliert, getestet – jetzt wird die Testsuite aus dem Prerequisites-Schritt gegen sie laufen gelassen. Fixer Agents untersuchen fehlgeschlagene Tests anhand beider Codebasen. Ein Build-Daemon ist der einzige Prozess, der das Binary neu bauen darf. Die Fixer schreiben Patches, der Daemon sammelt sie, baut einmal neu, führt die betroffenen Tests aus und liefert die Ergebnisse zurück. Das serialisiert den teuersten Vorgang.
Wiederholt sich derselbe Fehler in vielen Tests, wandert der Fix nach oben: Die Regel, die den Bug verursacht hat, wird geändert und nur die Dateien werden neu generiert, die diese Regel betrifft. Mikes Ansatz ist hier aufschlussreich: Da er keine portierte Testsuite hatte, ließ er Claude ein kleines Skript bauen, das sieben reale Szenarien gegen beide Versionen laufen ließ und die Ergebnisse per Diff verglich. Jedes fehlschlagende Szenario bekam einen eigenen Fix-Agenten. Dann ging er noch weiter: Claude entwarf eine eigene E2E-Testsuite und ließ sie autonom über Nacht laufen, reparierte, was kaputtging, und wiederholte das vier Nächte lang. So wurden auch die kleinen Fehler erwischt, die keine Szenarioliste vorhergesehen hätte. Fehlt eine Testsuite, baut Claude eine.
Best Practices für Code-Migrationen
Jede Migration lehrt etwas Neues. Einige Grundsätze haben sich bei Anthropic bewährt:
- Befolge diesen Leitfaden nicht blind. Jede Migration ist anders. Nutze die Struktur als Ausgangspunkt und plane deine spezifische Migration mit Claude, bevor du loslegst.
- Konzentriere dich nicht auf einzelne Fehler. Einzelfehler sind der Job der Fixer-Agenten. Deine Aufmerksamkeit gehört den Mustern.
- Mache Reviews gegnerisch und Verifikation mechanisch. Gegnerisches Review lohnt sich bei langlebigen Aufgaben oft. Lass Skripte – Compiler, Diff, Testsuite – den Schiedsrichter spielen.
- Nutze nicht das größte Modell für alles. Tokens kosten Geld, besonders in Schleifen. Kleinere Modelle bewältigen die Massenimplementierung gut; spare dir das große Modell für Reviewer und für alles, was Regeln schreibt, die andere Agenten befolgen.
- Investiere Zeit am Anfang. Das Regelbuch und der Stresstest sind der zeitaufwändigste Teil. Danach brennen im Wesentlichen die Warteschlangen ab.
- Mache die Warteschlange mechanisch und fortsetzbar. „Erledigt“ bedeutet: Die Ausgabedatei existiert auf der Festplatte.
Ergebnisse und Einordnung
Jarreds Bun-Migration läuft inzwischen in Produktion. Keine Migration ist perfekt: Etwa 4 % des Rust-Codes liegen in „unsafe“-Blöcken – meist einzeilige Zeigeroperationen an C/C++-Grenzen. Aber die neue Codebasis ist messbar besser. Alle vom Tooling erfassbaren Speicherlecks sind behoben: Ein Benchmark mit 2.000 Wiederholungen sank von 6.745 MB Speicher auf 609 MB. Das Binary ist auf Linux und Windows 19 % kleiner. Sprachübergreifende Optimierungen brachten 2–5 % mehr Geschwindigkeit bei HTTP-Serving und realen Workloads.
Wenn du vor einer ähnlichen Aufgabe stehst – Legacy-System in eine moderne Sprache überführen oder kritische Infrastruktur erneuern –, zeigt dieser Prozess, dass KI nicht nur beim Schreiben von Code hilft, sondern vor allem bei der Systematik. Die Kunst liegt nicht im Übersetzen, sondern im Entwerfen der Regeln und Prüfmechanismen. Der Rest ist Mechanik – und die kann man automatisieren.
Quelle: claude.com
