Refactor-Pass (Refactoring-Durchlauf)
Früher begann Refactoring mit einem Gefühl. Code war schwer zu bearbeiten: eine Klasse, durch die man scrollen musste, um zu finden, wo das Neue hingehört; eine Funktion, die man im Kopf behalten musste, um einen Zweig zu ändern; ein Feature, das den Besuch von acht Dateien bedeutete. Das gleiche Gefühl sagte einem, was man in Ruhe lassen sollte. Hässlicher Code, den man nie anfasste, tat nie weh, also ließ man ihn.
Ein Agent, der Code schreibt, spürt nichts davon – und auch ein Entwickler, der meist nur liest, was der Agent geschrieben hat, nicht. So häufen sich Gerüche unbemerkt. Ein Agent fügt einer Funktion einen dreißigsten Zweig hinzu, ohne zu murren. Und wenn er aufgefordert wird zu refactoren, räumt er auf, was unordentlich aussieht, einschließlich Code, den niemand anfasst. Diese Skill gibt beide Hälften des Gefühls zurück, als drei messbare Dinge:
- Wo du es spüren würdest: die Heatmap. Reibung ist nur dort wichtig, wo Änderungen ständig landen. Studien zufolge sagen Dateigröße und Änderungshäufigkeit Wartungsprobleme besser voraus als jeder einzelne Geruch.
- Was du spüren würdest: die Gerüche. Der etablierte Katalog von Gerüchen, jeweils übersetzt in das, was zu zählen ist, wann es kein Geruch ist, und das Playbook benannter Refactorings, das jahrzehntelange Praxis dafür aufgebaut hat: references/smells.md.
- Wie sehr du es spüren würdest: der Walkthrough. Nimm die nächste anstehende Änderung, geplant oder wahrscheinlich, und liste alles auf, was sie berühren würde.
Dann kombiniert das Urteil Wo und Was:
| Riecht | Riecht nicht | |
|---|---|---|
| Ändert sich oft | Refactoren | Sag, es ist in Ordnung |
| Ändert sich selten | In Ruhe lassen; höchstens ein "Später"-Hinweis | Nichts |
Das "Sag, es ist in Ordnung"-Kästchen ist genauso wichtig wie das "Refactoren"-Kästchen. Es ist die Zurückhaltungshälfte der Intuition, und der Bericht zeigt sie.
Zwei Größen von Fragen. Ein Durchlauf deckt das gesamte Repository ab: Schritte 1 bis 7. Eine Frage zu einem Bereich ("Sollten wir X refactoren?") führt die gleichen Schritte nur für diesen Bereich aus. Sie überspringt die Checkliste, es sei denn, es gibt mehrere Einträge, und ihre Antwort kann "Nein" sein. Führe in jedem Fall zuerst das Skript von Schritt 1 mit --checklists allein aus: Wenn es eine Checkliste aus einem früheren Durchlauf auflistet, lies zuerst Nach einem früheren Durchlauf.
1. Wo: die Heatmap
python3 <Verzeichnis dieser Skill>/scripts/heatmap.py -C <Repository> <Bereich ...>
<Verzeichnis dieser Skill> ist der Ordner, der diese SKILL.md enthält. Wo python3 nicht der Befehl ist, wie oft unter Windows, verwende python oder py.
- Bereich: die Quellordner und die Ordner der Tests, wie
src testsoderapp, nicht Docs oder Assets. Lies die README des Projekts, eine Verzeichnisauflistung und alle Agent-Anweisungen (AGENTS.md,CLAUDE.md), um zu entscheiden. Testdateien, identifiziert anhand des Namens (Board.spec.ts,test_x.py,XTest.php...) oder Ordners, bleiben außerhalb der Tabelle und der Paare und werden separat aufgeführt, wenn sie sich mit mehreren Dateien ändern. Füge--test GLOBfür andere Testnamen hinzu,--not-test GLOBfür eine Datei, die nur so aussieht (die Heatmap nennt alle, an denen sie zweifelt), und--ignore GLOBfür generierte Dateien. - Fenster: Standardmäßig die letzten 200 Commits.
--sincelegt es stattdessen fest, wenn der Benutzer einen Startpunkt nennt ("seit v2.0", "seit wir die Abrechnung hinzugefügt haben", "die letzten drei Monate"): ein Tag, Branch oder Commit oder ein Datum. Bot-Commits, die Bulk-Reformats, die ein Repository in.git-blame-ignore-revsauflistet, und die Commits, die auf früheren Checklisten aufgezeichnet wurden, werden automatisch ausgelassen.--exclude-commitslässt andere aus, die alles, was sie berührt haben, heiß aussehen lassen würden, wie einen fremden Import. - Zu dünn? Bei weniger als etwa 30 Commits im Fenster können die Zählungen heiße Dateien nicht von kalten unterscheiden. Erweitere das Fenster oder sage, die Geschichte ist zu kurz, um auf etwas hinzuweisen, und beurteile nur anhand der Gerüche. Ein flacher Klon (die Heatmap sagt es) benötigt
git fetch --unshallow; frage den Benutzer.
--help erklärt jede Spalte und Option.
Lesen der Heatmap
- Ein Hotspot hat viele Commits und ist auch groß, daher listet die Ausgabe diese zuerst: sortiert nach Commits mal Größe. Viele Commits für eine kleine Datei sind oft in Ordnung, wie eine Konfiguration, die für jedes Feature bearbeitet wird. Eine große Datei, die niemand anfasst, ist billig in Ruhe zu lassen.
- Noch heiß?
recentzählt seine Commits in der neueren Hälfte des Fensters (die Hälfte der Commits, nicht die Hälfte der Zeit), undlastist das Datum seines letzten Commits. Lies beide im Vergleich zu den Daten des Fensters: Achtrecent-Commits, alle an dem Tag, an dem die neuere Hälfte beginnt, sind ein Ausbruch, der abgekühlt ist. Eine Datei, die geschrumpft ist, wurde möglicherweise bereits behandelt. relsind geänderte Zeilen im Verhältnis zur Dateigröße. Relative Änderungsrate sagt Defekte weit besser voraus als absolute Änderungsrate. Deutlich über 1 bedeutet, dass die Datei im Fenster neu geschrieben oder gewachsen ist. Lies es zusammen mit growth: Eine Datei, die nur wächst, Commit für Commit, ist Additive-Only Growth. Eine im Fenster entstandene Datei hat ein growth gleich ihrer Größe.- Paare sind Dateien, die sich ständig in denselben Commits ändern. Eine Gruppe sind drei oder mehr, die alle miteinander paarweise verbunden sind, gezählt durch die Commits, die sie alle geändert haben; Gruppen, die sich überschneiden, werden als ein Bereich zusammen aufgeführt.
- Dateien aus verschiedenen Schichten, die stark paaren, oder eine Gruppe über Schichten hinweg bedeuten normalerweise ein Feature, das über sie verteilt ist (Shotgun Surgery).
- Überprüfe, was jedes Paar verbindet. Ein Import, ein Aufruf oder ein Vertrag, den etwas benennt (eine Route, ein Schema, die Typdatei zwischen einem Controller und seiner Seite) ist eine sichtbare Abhängigkeit: ein Feature über eine Grenze hinweg. Dateien, die sich ohne eine solche Verbindung zusammen ändern, wie zwei Kopien einer Regel oder zwei Listen, die von Hand synchron gehalten werden, haben eine versteckte Abhängigkeit, die stärkste Art von Kopplung.
- Eine Datei, die mit vielen anderen paart, ist ein Hub, durch den jede Änderung geht.
- Ein Test, der als sich mit mehreren Dateien ändernd aufgeführt ist, zusätzlich zu seiner eigenen, ist an deren Interna gebunden und wird Refactoring blockieren, es sei denn, diese Dateien sind eine Gruppe, die der Test nur verfolgt.
- Wenn viele Commits zu groß zum Paaren waren (
--max-files), wie in einem Repository, das Squash-Merges durchführt, erhöhe es oder lies die Paare als teilweise.
- Warum es sich ändert. Lies die Commit-Nachrichten (
--show) der obersten Dateien und gruppiere sie nach Feature. Viele unzusammenhängende Gründe zum Ändern einer Datei sind Divergent Change und zeigt sich hier direkter als in irgendeiner Zählung.
2. Was: die Gerüche
- Messe die heißen Dateien gegen die Codebasis selbst, innerhalb einer Sprache.
python3 <Verzeichnis dieser Skill>/scripts/measure.py -C <Repository> <Bereich ...>platziert jede Datei in einem Band nach dem 70., 80. und 90. Perzentil ihrer Sprache, und--show <Datei>jede ihrer Funktionen. Groß ist das 80. oder 90. Band (die Bänder wenden auch die Untergrenze des Katalogs an); das 70. ist einen genaueren Blick wert. Wo es sagt, dass die Perzentile auf eine Datei zurückzuführen sind, beurteile auch anhand der Referenzpunkte in Messen, das auch sagt, wie der Rest zu zählen ist (Verzweigungen, Parameter, Kohäsion) oder den Linter des Projekts zu verwenden. - Lies die obersten zwei oder drei Kerndateien selbst. Zahlen deuten an; Lesen entscheidet.
- Überprüfe die anderen heißen Bereiche. Wenn du Sub-Agenten ausführen kannst, gib zwei oder drei von ihnen jeweils einen Bereich (eine Schicht oder ein Verzeichnis, damit jeder einen gesamten Bereich einmal liest) und führe sie parallel aus. Weise jeden ein mit references/review-brief.md. Ohne Sub-Agenten überprüfe die Bereiche selbst, einen nach dem anderen, anhand desselben Briefings.
- Benenne jeden Geruch mit seinem etablierten Namen (Long Function, Shotgun Surgery, Repeated Switches…) und überprüfe ihn gegen den Eintrag "Kein Geruch, wenn", bevor er zählt. Der Name ist der Index zu seinem Playbook.
- Suche nach der fehlenden Idee. Gerüche treten in Clustern auf, und ein Cluster weist normalerweise auf eine Sache hin, die der Code ständig verwendet, aber nie benannt hat: einen Zustand, die Definition einer Art, eine einzige Quelle für eine Einstellung. Siehe Gerüche reisen zusammen.
- Überprüfe, welche Tests den heißen Code erreichen: siehe Ungetesteter Code.
- Fehler, die dir auffallen, werden als F-Einträge auf die Liste gesetzt, jetzt nicht behoben. Ein noch nicht reproduzierter Fehler ist ein Verdacht: Betitle ihn als solchen ("Prüfen: Bearbeitungen überspringen die Wochenendregel"), mit den Beweisen in seiner Notiz. Sein erster Schritt ist ein fehlschlagender Test; wenn keiner fehlschlägt, wird er übersprungen, mit dem, was du gefunden hast.
3. Wie viel: der Walkthrough
Das ist das, was dem alten Gefühl am nächsten kommt. Es ist auch das, was einen Eintrag überzeugend macht.
- Finde die nächsten Änderungen. Eine geplante Änderung ist besser als eine abgeleitete, also nimm sie zuerst von dem, was der Benutzer gesagt hat, dass ansteht ("bevor wir mit Chunk 7 beginnen"), dann aus dem Plan oder der Roadmap des Projekts, falls vorhanden, dann aus den Commit-Nachrichten: die Arten von Änderungen, die am häufigsten vorkommen ("einen neuen Berichtstyp hinzufügen", "eine Einstellung hinzufügen", "einen Bildschirm hinzufügen"). Nimm die zwei oder drei häufigsten Arten, nicht nur eine.
- Für jede Art nimm den letzten echten Commit dieser Art und liste auf, was er berührt hat (
git show --stat). Wenn er älter als das Fenster ist, verwende ihn trotzdem und gib sein Datum an; der Walkthrough zeigt im heutigen Code, was sich seitdem bewegt hat. Wenn die Dokumentation oder Agent-Anweisungen des Projekts ein Rezept für diese Änderung haben, zähle seine Schritte. Ein Fünf-Schritte-Rezept zum Hinzufügen einer Sache ist ein Eingeständnis. - Gehe die nächste Änderung durch, ohne sie zu schreiben. Liste jede Datei auf, die du lesen müsstest, jede Stelle, die du bearbeiten würdest, und jede Kopie, die du synchron halten würdest.
- Schreibe die Anzahl heute und die Anzahl nach dem Refactoring auf, das du vorschlagen würdest: "Ein neuer Berichtstyp berührt heute 7 Dateien und 3 Switches und würde nachher 1 Datei berühren."
Eine kalte Datei, in der geplante Arbeit landen wird, ist nicht mehr kalt: Setze sie in die obere Zeile der Tabelle, da die nächste Änderung bezahlt, was immer sie kostet. Wenn die nächste Änderung heute billig ist, kostet der Geruch wenig, egal wie er aussieht.
4. Überprüfe jeden Befund
Ein Befund eines Reviewers ist ein Hinweis, keine Tatsache, und das Gleiche gilt für deine eigenen ersten Eindrücke. Bevor etwas auf die Liste kommt, öffne die zitierten Zeilen, führe die Zählungen erneut aus und streiche oder korrigiere, was nicht Bestand hat. Sage im Bericht, was du überprüft hast. Ein falscher Eintrag, der den Benutzer in ein Refactoring schickt, kostet weit mehr als die Minute, die es braucht, um ihn zu überprüfen.
Dann wende die Zurückhaltung an. Ein Reviewer, der gebeten wird, Probleme zu finden, wird immer etwas finden, und das Beheben jedes Befunds hinterlässt zusätzliche Ebenen und Tests für Fälle, die nicht eintreten können. Lasse weg, was kalt ist, was die Zeilen "Kein Geruch, wenn" entschuldigen und was der Walkthrough als billig ausweist.
5. Ordnen
Gib jedem Eintrag eine stabile ID, da der Benutzer Einträge mit dieser ID benennen wird ("Starte R4"), möglicherweise in einer späteren Sitzung. Die Namen in eckigen Klammern sind die Felder des Eintrags auf der Checkliste (Schritt 6); F-, L- und D-Einträge geben ihre in einer note an.
- R1, R2… Refactorings, in der vorgeschlagenen Reihenfolge. Für jedes:
- ein Titel, der die fehlende Idee als Ergebnis benennt ("Benenne die Zustände eines Checkouts", nicht "Refactoriere CheckoutController") (
title); - die Gerüche namentlich mit Zahlen und
datei:zeile(smells); - die Art der Änderung, die es erleichtert, aus Schritt 3 (
eases), und die Kosten dieser Änderung (cost:todayundafter); - der Plan, als die Schritte des Playbooks (
plan); - die Dateien, Größe (S, M oder L) und Risiko (Niedrig, Mittel oder Hoch) (
files,size,risk); - wie zu überprüfen ist, dass sich das Verhalten nicht geändert hat (
verify).
- ein Titel, der die fehlende Idee als Ergebnis benennt ("Benenne die Zustände eines Checkouts", nicht "Refactoriere CheckoutController") (
- F1… Kleine Fixes, die nebenbei gefunden wurden, jeder sein eigener Commit.
- L1… Später: stinkend aber kalt, oder es lohnt sich, wenn der nahe Code das nächste Mal angefasst wird.
- D1… Entscheidung des Benutzers: Änderungen an seiner Arbeitsweise, wie eine Docs-Konvention, die jede Änderung teuer macht. Lege den Trade-off dar, keine Entscheidung.
Ordne nach Nutzen im Verhältnis zum Risiko. Nutzen ist, wie sehr die Änderung zukünftige Arbeit erleichtert: Die Heatmap und der Walkthrough sind die Beweise. Die Prioritäten der Gerüche sagen, welche Arten tendenziell wichtig sind. Langer und komplexer Code sowie auf mehrere Dateien verteilte Änderungen sind am wichtigsten. Kopplungsgerüche wie Feature Envy und Middle Man sind am unwichtigsten und werden im Vorbeigehen behoben.
Ordne die Liste so, dass:
- Blocker zuerst kommen: Tests, die an Interna gebunden sind, die jeden späteren Schritt so aussehen lassen würden, als hätte er etwas kaputt gemacht, und Code, der von keinem Test abgedeckt wird, dessen Eintrag mit dem Festlegen von Tests beginnt oder klar sagt, dass er nicht überprüft werden kann (Schritt 7);
- die kleinen Einträge, die einen Copy-Paste-Pfad entfernen, als nächstes kommen;
- große Bewegungen danach kommen, da sie oft zuerst eine Naht benötigen;
- alles, was du nicht selbst überprüfen kannst (wie es klingt, sich anfühlt oder aussieht), kommt zuletzt, weil der Benutzer es überprüfen muss.
6. Checkliste und Bericht schreiben
Führe den Befehl von Schritt 1 erneut aus mit --page .refactor-pass/<YYYY-MM-DD>.html, mit dem heutigen Datum. Es erstellt die Checkliste aus assets/page.html und schreibt die Heatmap in ihren heatmap-Block; bei erneuter Ausführung aktualisiert es die Heatmap und sonst nichts. Du schreibst den pass-Block darunter, dessen project, started, base und updated bereits ausgefüllt sind:
summary: der Gesamtzustand in zwei oder drei Sätzen;items: jeder Eintrag mit den Feldern von Schritt 5, einemstatus(todo,doing,waitingauf den Benutzer,done, oderskippedmit dem Grund in seinemoutcome), undcommitsundoutcome, leer für einen neuen Eintrag; ein übernommener Eintrag behält seinfrom. Jeder Eintrag beginnt mit seiner ID und seinem Status in einer Zeile,{"id": "R1", "status": "todo",, mit seinen anderen Feldern danach, wie der Kommentar über den Blöcken vollständig zeigt; nicht wie ein JSON-Pretty-Printer es anordnet, da die Statusaktualisierungen von Schritt 7 diese eine Zeile bearbeiten;left_alone: jeder mitwhatundwhy;checks: die Prüfungen des Projekts aus seinen Anweisungen oder was du von Hand überprüfen wirst, wenn es keine hat;earlier: nach einem früheren Durchlauf seinechecklistundresults, eine pro darauf abgeschlossenem Refactoring: seineid,title,easesundcost, die tatsächlichechange, die als nächstes kam, und was sieactualberührt hat (beide leer, wenn noch keine gekommen ist), und einenote. Sonstnull;log: jeder mit einemdateundtext, neueste zuerst.
Nach jeder Bearbeitung führe python3 <Verzeichnis dieser Skill>/scripts/check_page.py <Seite> aus, das eine kaputte Klammer, ein falsch geschriebenes Feld, eine doppelte ID oder einen Eintrag, der nicht in einer Zeile beginnt, erkennt. Wenn deine Umgebung HTML-Seiten veröffentlichen oder in der Vorschau anzeigen kann, zeige dem Benutzer den Bericht. Sage im Bericht, wann der nächste Schritt (Schritt 7) den Status aktualisiert.
7. Status aktualisieren
Wenn der Benutzer einen Eintrag startet, setze seinen Status auf doing. Wenn er abgeschlossen ist, ordne die Commits den Einträgen zu (mehrere Commits pro Eintrag oder mehrere Einträge pro Commit; ein Commit, der Teile von zwei Einträgen erledigt, sollte in zwei Einträgen aufgeführt werden), setze seinen Status auf done und schreibe in sein outcome, was es jetzt leichter macht (Test, Geruchszahl oder Walkthrough-Zahl; drei Zeilen genügen). Wenn der Benutzer bestätigt, dass ein geplantes Ticket mit einem Eintrag behandelt wurde, aktualisiere seinen outcome mit dem Ticket und jedes later mit einer Notiz.
Wenn ein Eintrag auf den Benutzer wartet, setze ihn auf waiting mit dem, worauf du wartest.
Beginne den Bericht mit einem Log-Eintrag für das, was gerade passiert ist.
Nachdem der Eintrag erledigt ist
Wenn der Benutzer den nächsten Eintrag von der Liste startet oder um einen weiteren Durchlauf bittet, aktualisiere die Heatmap (Schritt 1) und führe die Zählungen erneut aus oder messe die geänderten Dateien mit dem Messskript und gehe zu Schritt 3 (Schritt 2 erneut ausführen, nur wenn du tatsächlich relevanten Code gelesen hast). Der Walkthrough ist jetzt teilweise real; schreibe die tatsächlichen Kosten dessen, was du gerade getan hast, in den earlier-Block und notiere, ob sich die Kosten des nächsten Tickets geändert haben. Alle Einträge, die noch auf todo stehen, sind die gleichen, es sei denn, der Geruch wurde verringert; in diesem Fall zeige sowohl die alte als auch die neue Anzahl.
Nach einem früheren Durchlauf
Die Checkliste existiert bereits. Lies den neuesten und aktualisiere seinen log, updated und seine heatmap. Beginne mit den Einträgen aus dem vorherigen Durchlauf; füge neue hinzu, wenn der Benutzer ein neues Problem nennt. Lasse die IDs der neuen als nächste Nummer für ihren Buchstaben: Wenn der vorherige Durchlauf R1 und R2 hatte, ist der nächste R3. Alles, was auf dem vorherigen Durchlauf auf waiting stand und immer noch so ist, behalte es; alles, was der Benutzer als erledigt markiert hat, lasse es. Prüfe, ob ein later-Eintrag jetzt warm genug für ein Refactoring ist und ob das Projekt seine Checks geändert hat.
Referenzmaterial
Der Geruchskatalog, das Review-Briefing und die Faustregeln für Playbooks:
- references/smells.md – Jeder benannte Geruch mit Definition, wann es kein Geruch ist, und der Playbook-Fallback-Reihenfolge.
- references/review-brief.md – Briefing, das du für Sub-Agenten oder dich selbst verwendest.
- references/playbook-notes.md – Faustregeln, die die Playbooks gemeinsam haben.
Die Heatmap, das Messskript und das Tools-Seitenskript:
Die HTML-Seite, die die Checkliste rendert: