Free

Claude refactoren lassen

Refactorings haben keine Symptome — du siehst nicht, wenn Claude falsch liegt. Drei Leitplanken: Tests, atomare Commits, manueller Klick.


Refactoring ist die Aufgabe, bei der Claude am gefährlichsten ist. Ein Bug hat Symptome — Button reagiert nicht, Wert ist undefined, Stacktrace — also kannst du beurteilen, ob Claudes Fix stimmt. Ein Refactoring hat keine. „Läuft immer noch" kann heißen „Tests sind immer noch grün", während sich das Verhalten still verändert hat, du es nicht bemerkt hast und die Produktion eine Woche später in Flammen steht.

Ich habe kürzlich mit Claude ein ziemlich großes Refactoring in how2claude gemacht: die x402-Krypto-Zahlungen von einem handgeschriebenen PaymentHandler + FacilitatorClient (139 Zeilen) auf das Gem x402-rails migriert, gleichzeitig das Feld-Mapping von Purchase.create! / Subscription.create! — in zwei Controllern dupliziert — in Model-Klassenmethoden extrahiert. Ein Commit, 4 Dateien geändert, 2 gelöscht, 2 hinzugefügt.

Mein Prompt war ein Wort: „refactor."

So kurz war er möglich, weil drumherum Leitplanken standen.

Leitplanke #1: Kein Refactoring ohne Tests

Als der Branch dort ankam, hatte er 221 Tests. Alle kritischen Pfade im Zahlungsfluss waren abgedeckt.

Claudes Default-Aktion vor einem Refactoring ist nicht „schau zuerst in die Tests." Also sagte ich ihm, er solle zuerst bin/rails test ausführen, Grün bestätigen, und danach etwas anfassen.

Nach dem Refactoring erneut laufen lassen. Immer noch grün. Das bedeutet nicht „keine Regression" — es bedeutet, bekanntes Verhalten ist nicht kaputt.

Wenn dein Codepfad keine Testabdeckung hat, lass Claude einen minimalen Test schreiben, der das aktuelle Verhalten festzurrt. Lauf lassen, commiten. Dann refactoren. Sonst ist das, was er macht, kein Refactoring, sondern Rewrite — und du hast keine Möglichkeit, Äquivalenz zu prüfen.

Leitplanke #2: Lass ihn die Änderung in atomare Commits aufteilen

Dieses Refactoring waren eigentlich zwei Dinge:

  1. x402-Backend: handgeschrieben → Gem
  2. Feld-Mapping von Purchase / Subscription: Controller → Model-Klassenmethoden

Im Frontend gab es ein drittes: den JS-seitigen Signing-Flow mit viem + x402-fetch neu schreiben.

Ich ließ Claude nach natürlichen Grenzen aufteilen: Backend + Model-Extraktion in einem Commit (9f3e239), Frontend in einem separaten (93746d8). Jeder Commit trägt eine vollständige Beschreibung, die Dateiliste und die Begründung der Änderung.

Vorteile:
- Diffs bleiben lesbar. Ein Commit, eine Sache.
- Rollback-Granularität. Wenn die Produktion einen Frontend-Bug hat, rollt git revert 93746d8 nur das Frontend zurück, das Backend bleibt.
- Claudes eigene Aufmerksamkeit bleibt fokussiert. Ein Commit, eine Sache — und seine Aufmerksamkeit deckt nur diese eine Sache ab.

Leitplanke #3: Den Diff lesen, bevor du „fertig" sagst

Wenn das Refactoring fertig ist, lasse ich Claude stoppen und mir git diff --staged zeigen. Keine Tests laufen lassen, die App nicht starten, erst den Diff lesen.

Signale, die ich scanne:

  • Was hat er gelöscht? app/services/x402/payment_handler.rb komplett gelöscht — gut, das ist der Sinn der Gem-Migration. Aber wenn er etwas löscht, was ich nicht angefasst haben wollte, halte ich an und frage nach.
  • Hat sich das Feld-Mapping geändert? Purchase.create!(wallet_address: verify_result["payer"], ...)Purchase.record_x402!(payment:, settlement:) liest jetzt payment[:payer]. Die Quelle hat sich geändert (request.env des Gems vs. Rückgabewert des alten Clients), aber die Felder müssen eins-zu-eins zusammenpassen.
  • „Am Rande" gemachte Änderungen. Claude liebt es, beim Refactoring Dinge zu „korrigieren", die „falsch aussehen" — Fehlermeldungen umformulieren, Variablen umbenennen, eine Methode extrahieren, die seiner Meinung nach existieren sollte. Darauf aufpassen. Das Versprechen eines Refactorings ist „verhaltensäquivalent". Eine beiläufige Änderung bricht das.

Zwei Dinge, die Claude diesmal falsch gemacht hat

Falle 1: Die Stimulus-Controller des Gems wurden still nicht geladen

Das Gem x402-rails bringt eigene Stimulus-Controller mit. Claude schrieb den Code, die Tests liefen grün. Ich klickte manuell auf den Bezahl-Button — nichts.

Grund: In config/importmap.rb zeigte der Pin für @hotwired/stimulus auf eine nicht existierende Vendor-Datei, und importmap verwarf den Pin still. Die Controller des Gems wurden nie geladen. Tests können das nicht fangen, weil bin/rails test kein JS ausführt.

Falle 2: YAML parste 0x... als Integer

wallet_address: 0x833589... — keine Anführungszeichen. YAML sah das Präfix 0x, las es als hexadezimalen Integer. Der Facilitator bekam einen Nicht-String und lehnte ab. Claude hielt beim Schreiben der Config nicht inne, um über YAML-Parsing-Regeln nachzudenken.

Beide Fallen wurden erwischt, weil ich den echten Button geklickt habe. Grüne Tests sind nicht dasselbe wie eine funktionierende Feature. Manuelle Verifikation nach einem Refactoring ist nicht optional.

Der vollständige Claude-Refactoring-Flow

  1. Die gesamte Test-Suite laufen lassen. Grün bestätigen. Wenn der Zielcode keine Abdeckung hat, lass Claude einen minimalen Test schreiben, der das aktuelle Verhalten festzurrt, und committe den.
  2. Sag, welche Fläche du extrahieren willst. „Refactor" reicht, wenn die Duplikation offensichtlich ist; sonst sag „extrahiere das Feld-Mapping von X in Klassenmethoden auf Y."
  3. In atomare Commits aufteilen. Ein Commit, eine Sache.
  4. Lies den Diff selbst. Was gelöscht wurde, ob das Feld-Mapping hält, ob am Rande etwas geändert wurde.
  5. Tests nochmal laufen lassen. Grün.
  6. Wenn ein nutzersichtbarer Pfad berührt wurde, klicke die Feature manuell durch. Schichten, die Tests nicht erreichen — JS, importmap, CDN, YAML-Parsing — musst du mit eigenen Augen sehen.

Refactoring ist das „Claude fahren lassen"-Szenario mit dem höchsten Risiko. Die Leitplanken sind nicht für Claude. Sie sind für dich — damit du, wenn Claude etwas falsch macht, es in 5 Minuten bemerkst, statt erst im Produktionsbrand.