2.19

View in English

2.19 Refactoring und technische Schulden

Überblick und Motivation

Refactoring ist, die interne Struktur von Code zu ändern, ohne zu ändern, was er von außen tut. Sie benennen eine Variable um, teilen eine lange Funktion, extrahieren eine Klasse, kollabieren ein Gewirr von Bedingungen in etwas, dem eine Leserin folgen kann, und das Programm verhält sich exakt wie zuvor. Dieser letzte Teil ist die ganze Disziplin. Refactoring ist per Definition verhaltensbewahrend, und in dem Moment, in dem Sie auch Verhalten ändern, refaktorieren Sie nicht mehr, Sie tun zwei riskante Dinge auf einmal und verstecken jedes hinter dem anderen. Dieses Kapitel behandelt diese absichtlich als getrennte Akte, denn die Verwechslung zwischen ihnen ist, wo die meisten Refactorings falsch laufen.

Bei einem großen Team zählt das mehr als bei einer Solo-Entwicklerin, denn der Code, den Sie aufräumen, ist Code, den Hunderte andere Menschen lesen, von dem sie abhängen, und den anzufassen sie sich fürchten. Refactoring ist, wie eine geteilte Codebasis über Jahre und Personalwechsel hinweg bewohnbar bleibt. Es verbindet sich direkt mit Softwarekonstruktion (Kapitel 2.9), wo alltägliche Codequalität gesetzt wird, und mit Teststrategie (Kapitel 2.4), dem Sicherheitsnetz, das Refactoring überhaupt sicher macht. Es verbindet sich auch mit der schwierigeren Frage technischer Schulden: den angehäuften Kosten von Abkürzungen, alternden Gestaltungen, und aufgeschobener Aufräumung, die jede zukünftige Änderung langsamer machen. Refactoring ist der Hauptweg, diese Schulden abzuzahlen, die zwei Themen gehören also in ein Kapitel.

In Unternehmens- und Behördenumgebungen steigen die Einsätze. Diese Systeme sind langlebig, oft Jahrzehnte alt, und häufig unter Prüf- und Änderungskontrollregimen, die jede Codeänderung als geregeltes Ereignis behandeln. Sie können ein Bürgerleistungssystem nicht einfach über ein langes Wochenende umschreiben; Sie modernisieren es in kleinen, umkehrbaren, beleggestützten Schritten, was genau ist, was diszipliniertes Refactoring gibt. Diese Arbeit über viele Teams und langlebige Systeme (Kapitel 10.4) zu koordinieren ist eine der definierenden Herausforderungen großmaßstäblichen Ingenieurwesens, und es falsch zu machen ist, wie Organisationen erstarrt enden, unfähig, Software zu ändern, die sie nicht mehr verstehen.

Kernprinzipien

  • Refactoring bewahrt Verhalten; wenn Sie ändern, was der Code tut, ist das eine separate Änderung, separat gemacht.
  • Eine vertrauenswürdige Testsuite ist die Voraussetzung für sicheres Refactoring, kein optionales Extra.
  • Arbeiten Sie in kleinen, benannten, umkehrbaren Schritten, und halten Sie den Code nach jedem funktionierend.
  • Machen Sie technische Schulden sichtbar und verfolgt, finanzieren Sie dann Abzahlung als stetige Kapazität statt Heldentum.
  • Refaktorieren Sie den Code, den Sie bereits ändern, wo sich die Aufräumung auszahlt.
  • Nicht alle Schulden sind es wert, abgezahlt zu werden; stabiler, selten berührter, oder bald pensionierter Code kann in Ruhe gelassen werden.
  • Messen Sie interne Qualität, um Urteil zu informieren, nie als Ziel, das ausgetrickst wird.

Empfehlungen

Refactoring und Verhaltensänderung strikt getrennt halten

Entscheiden Sie, bevor Sie beginnen, welches von beiden Sie tun, und verwischen Sie die zwei nie in einem einzelnen Commit. Wenn Sie refaktorieren, müssen die Tests, die vorher bestanden, unverändert danach bestehen, denn das beobachtbare Verhalten hat sich nicht bewegt. Wenn Sie Verhalten ändern, tun Sie das als eigenen Commit mit eigenen Tests. Der Grund ist praktisch: wenn eine gemischte Änderung etwas bricht, können Sie nicht sagen, ob Ihre Umstrukturierung den Fehler einführte oder Ihre Verhaltensänderung, und in Code-Review (Kapitel 2.5) kann eine Prüferin über keine der beiden Hälften sauber nachdenken. Die Gewohnheit, die funktioniert, ist die Zwei-Hüte-Regel von Martin Fowler: Sie tragen immer entweder den Refactoring-Hut oder den Feature-Hut, Sie wissen welchen, und Sie wechseln absichtlich. Getrennte Commits machen auch die Versionskontroll-Geschichte lesbar, sodass eine Ingenieurin, die ein Scheitern bisektiert, die reinen Refactoring-Commits mit Vertrauen überspringen kann.

Ein vertrauenswürdiges Sicherheitsnetz etablieren, bevor Sie umstrukturieren

Refactoring ohne Tests ist nur Bearbeiten und Hoffen. Bevor Sie irgendetwas von Bedeutung umstrukturieren, brauchen Sie eine Suite, der Sie vertrauen, eine Verhaltensänderung zu erwischen, falls Sie eine verursachen, was das Kernargument der Teststrategie (Kapitel 2.4) ist. Für Code, der bereits gute Abdeckung hat, führen Sie die Tests aus, refaktorieren Sie in kleinen Schritten, und führen Sie sie nach jedem Schritt erneut aus. Für Legacy-Code ohne Tests ist der ehrliche Zug, zuerst Charakterisierungstests zu schreiben. Ein Charakterisierungstest behauptet nicht, was der Code tun sollte; er erfasst, was der Code tatsächlich gerade jetzt tut, einschließlich seiner Eigenheiten, sodass jede Änderung im Verhalten als scheiternder Test erscheint. Michael Feathers popularisierte diesen Ansatz genau für die Situation, in der große Organisationen leben: Code, der funktioniert, zählt, und keine Tests hat. Sobald das aktuelle Verhalten festgenagelt ist, können Sie darunter sicher refaktorieren, und erst dann Verhalten obendrauf ändern.

Lernen, Code-Gerüche zu erkennen und kleine benannte Refactorings anzuwenden

Ein Code-Geruch ist ein Oberflächenzeichen, dass etwas darunter vielleicht Aufmerksamkeit braucht: eine Funktion, die zu lang gewachsen ist, eine Klasse, die zu viel weiß, doppelte Logik, eine lange Parameterliste, Namen, die darüber lügen, was sie tun. Ein Geruch ist ein Hinweis, kein Urteil, Sie untersuchen also, statt ihm blind zu gehorchen. Die Antwort ist ein kleines, benanntes Refactoring aus Fowlers Katalog: Funktion extrahieren, Variable umbenennen, Methode verschieben, Bedingung durch Polymorphismus ersetzen, und Dutzende mehr. Der Wert, benannte Züge zu nutzen, ist, dass jeder klein, verstanden, mechanisch sicher, und oft direkt von Ihrer IDE unterstützt ist. Sie setzen große Verbesserungen aus vielen winzigen verlässlichen Schritten zusammen, den Code durchgehend grün haltend, statt einen großen Sprung zu machen, den Sie nicht verifizieren können.

Opportunistisches Refactoring bevorzugen und Kampagnen für echten strukturellen Bedarf reservieren

Das meiste Refactoring sollte opportunistisch sein, in die Arbeit gefaltet, die Sie bereits tun. Die Pfadfinder-Regel fasst es: hinterlassen Sie den Code ein wenig sauberer, als Sie ihn fanden. Wenn Sie eine Datei berühren, um ein Feature hinzuzufügen oder einen Fehler zu beheben, verstehen Sie diese Ecke bereits, und kleine Aufräumungen dort summieren sich über Zeit, ohne die Erlaubnis irgendjemandes oder ein separates Budget zu brauchen. Geplante Refactoring-Kampagnen, wo ein Team Feature-Arbeit stoppt, um einen großen Bereich umzustrukturieren, sind manchmal nötig, aber sie sind teuer, schwer gegen Produktdruck zu planen, und riskant, wenn der Bereich schlecht getestet ist. Reservieren Sie Kampagnen für strukturelle Probleme, die opportunistische Aufräumung nicht erreichen kann, und machen Sie den Fall mit Beleg über die Änderungskosten, die Sie bezahlen. Bevorzugen Sie das stetige Tröpfeln kleiner Aufräumungen; es ist dauerhafter als die gelegentliche heldenhafte Umschreibung.

Das Würgefeige-Muster für große strukturelle Änderung nutzen

Wenn ein ganzes Subsystem ersetzt werden muss, versuchen Sie keine Big-Bang-Umschreibung, die ein Jahr läuft und am Ende mergt; so sterben Modernisierungsprojekte. Nutzen Sie das Würgefeige-Muster, von Martin Fowler nach der Ranke benannt, die um einen Baum wächst und ihn schrittweise ersetzt. Sie setzen eine Fassade vor das alte System, leiten eine Funktionsscheibe nach der anderen zu neuem Code hinter dieser Fassade, verifizieren sie in Produktion, und wiederholen, bis das alte System vollständig umgeben ist und entfernt werden kann. Jede Scheibe ist klein, ausliefer- und umkehrbar, das Risiko bleibt also begrenzt und Wert kommt kontinuierlich an. Ein enger Verwandter, Zweig durch Abstraktion, tut dasselbe innerhalb einer einzelnen Codebasis: Sie führen eine Abstraktionsschicht über das ein, was Sie ersetzen wollen, bauen die neue Implementierung dahinter, während beide koexistieren, schalten Konsumenten schrittweise um, und löschen die alte Implementierung, sobald nichts mehr von ihr abhängt. Beide lassen ein Legacy-System sich entwickeln, während es am Leben bleibt, was die einzige Art Modernisierung ist, die sich die meisten großen Organisationen tatsächlich leisten können.

Technische Schulden als Portfolio behandeln und sichtbar machen

Die Schuldenmetapher, geprägt von Ward Cunningham, trennt zwei Dinge: die Hauptsumme (der unordentliche Code oder die Abkürzung selbst) und die Zinsen (der zusätzliche Aufwand, den jede zukünftige Änderung wegen ihr bezahlt). Nicht alle Schulden sind gleich. Fowlers Quadrant sortiert sie entlang zweier Achsen: absichtlich versus unabsichtlich, und umsichtig versus rücksichtslos. Umsichtig-absichtliche Schulden (“wir liefern jetzt aus und räumen nächsten Sprint auf, und wir kennen die Kosten”) sind eine legitime Geschäftsentscheidung. Rücksichtslos-unabsichtliche Schulden (“was ist ein Entwurfsmuster?”) sind einfach Schaden. Die Verwaltungsaufgabe, die sich mit Entscheidungsfindung und Governance (Kapitel 1.5) und deren Behandlung von Schulden als Portfolio verbindet, ist, die Schulden sichtbar zu machen, sodass darüber nachgedacht werden kann: verfolgen Sie bedeutende Posten, wo die Arbeit lebt, taggen Sie den Code, und zeichnen Sie die Zinsen auf, die Sie zahlen, sodass Abzahlung um Kapazität auf Beleg konkurriert statt darum, wer am lautesten klagt. Schulden, die Sie nicht sehen können, können Sie nicht verwalten.

Abzahlung als stetige Kapazität finanzieren, nicht als Heldentum

Der Scheitermodus ist, Aufräumung als etwas zu behandeln, das Sie tun werden, “wenn sich die Dinge beruhigen”, was nie ist. Das dauerhafte Muster ist eine feste, geschützte Kapazität für Abzahlung: eine explizite Scheibe jedes Zyklus, oder eine stehende Vereinbarung, dass Aufräumung mit Feature-Arbeit im selben Bereich mitreist. Was nicht funktioniert ist der periodische heldenhafte Sprint, wo jemand ein Wochenende verbrennt, um alles zu beheben, weil er nicht nachhaltig, ungeprüft, und normalerweise selbst rückgängig ist. Stetige Kapazität hält Zinszahlungen niedrig und vermeidet den Boom-Bust-Zyklus, wo Schulden sich anhäufen, bis eine Krise eine teure Umschreibung erzwingt. Das ist eine Managementverpflichtung so sehr wie eine Ingenieurspraxis, und es gehört dazu, wie Sie Softwarewartung (Kapitel 3.7) über die Lebensdauer eines Systems planen.

Interne Qualität messen, aber das Maß nicht zum Ziel werden lassen

Sie können interne Qualität mit Signalen wie zyklomatischer Komplexität (eine Zählung unabhängiger Pfade durch eine Funktion), Duplikation, Testabdeckung, Änderungsscheiterrate, und wie lange Änderungen in den Bereichen dauern, die Sie vermuten, messen. Diese Zahlen sind nützlich, um zu erkennen, wo sich Schulden konzentrieren, und um einen Trend über Zeit zu beobachten. Die Gefahr ist Goodharts Gesetz: wenn ein Maß zu einem Ziel wird, misst es nichts Echtes mehr. Verlangen Sie eine Abdeckungszahl und Sie bekommen Tests, die nichts behaupten; belohnen Sie niedrige Komplexitätswerte und Sie bekommen Logik, über mehr Funktionen verschmiert, um die Kennzahl auszutricksen. Nutzen Sie Kennzahlen, um Gespräche zu beginnen und Hotspots zu lokalisieren, und verdrahten Sie nie eine Qualitätskennzahl mit einem Tor, für dessen Austricksen Menschen motiviert sind.

Wissen, wann man nicht refaktoriert

Refactoring ist eine Investition, und manch Code wird sie nie zurückzahlen. Wenn ein Modul stabil ist, selten berührt, und gut genug verstanden, um es bei den seltenen Gelegenheiten zu ändern, in denen Sie müssen, ist es aufzuräumen Aufwand, ausgegeben für Zinsen, die Sie nicht zahlten. Wenn Code zur Pensionierung ansteht, ihn zu refaktorieren ist, etwas zu polieren, das Sie gleich wegwerfen. Die Disziplin ist, Ihr Aufräumbudget dort auszugeben, wo Änderung häufig und schmerzhaft ist, wo Zinsen zu reduzieren sich tatsächlich summiert, und die ruhigen Ecken in Ruhe zu lassen.

Abwägungen: Vor- und Nachteile

AnsatzVorteileNachteile
Opportunistisches Refactoring (Pfadfinder-Regel)Günstig, kontinuierlich, kein separates Budget, summiert sich über ZeitUngleichmäßige Abdeckung; heiße Dateien verbessern sich, während kalte verrotten
Geplante Refactoring-KampagneBehebt strukturelle Probleme, die Aufräumung nicht erreichen kannTeuer; konkurriert mit Features; riskant ohne gute Tests
Würgefeige / Zweig durch AbstraktionInkrementell, umkehrbar, hält System lebendig, begrenzt RisikoLangsamer als eine Umschreibung auf dem Papier; braucht Disziplin zum Beenden
Big-Bang-UmschreibungReines Blatt; keine Legacy-BeschränkungenHohe Scheiterrate; lange Zeit bis zum Wert; Verhaltenslücken
Absichtliche umsichtige SchuldenLiefert Wert jetzt; expliziter, geplanter ErtragWird rücksichtslos, wenn der Ertrag nie eingeplant wird
Kennzahlgetorte QualitätObjektiv, sichtbar, erwischt Abdrift frühLädt zu Austricksen ein; bestraft Nuance; kann echte Qualität senken

Die zentrale Spannung ist Geschwindigkeit jetzt gegen Veränderbarkeit später, und sie ist echt. Eine Abkürzung auszuliefern kann die richtige Entscheidung sein, wenn der Termin echt ist und die Schulden umsichtig und verfolgt sind. Der Fehler ist vorzugeben, dass die Schulden kostenlos sind, oder sie unsichtbar sich anhäufen zu lassen, bis das System zu teuer zum Ändern ist. Lösen Sie es, indem Sie den Tausch jedes Mal explizit machen: benennen Sie die Schulden, schätzen Sie die Zinsen, entscheiden Sie absichtlich, und zeichnen Sie die Entscheidung auf, damit Abzahlung geplant statt vergessen werden kann. Ein Team, das wissentlich leiht und stetig zurückzahlt, bleibt Jahre schnell; ein Team, das blind leiht, mahlt zum Stillstand.

Fragen zur Diskussion mit Ihrem Team

  1. Wie halten wir Refactoring und Verhaltensänderung davon ab, in denselben Commit zu bluten, und erzwingt unsere Prüfung das tatsächlich? Das ist die grundlegende Disziplin des ganzen Kapitels, und es ist die am häufigsten unter Termindruck verletzte, weil es sich effizient anfühlt, “das aufzuräumen, während ich hier drin bin” und alles zusammen auszuliefern. Die Kosten landen später: wenn ein gemischter Commit Produktion bricht, kann niemand sagen, ob die Umstrukturierung oder das Feature es verursachte, und eine Bisektion durch Ihre Geschichte hört auf, vertrauenswürdig zu sein. Bringen Sie eine Handvoll jüngster Pull-Requests und prüfen Sie ehrlich, wie viele die zwei Hüte mischten. Die konkurrierende Erwägung ist Reibung, da Arbeit in separate Commits zu teilen ein wenig mehr Aufwand vorab ist. Die Antwort sollte Ihre Commit-Konventionen und Ihre Prüf-Checkliste formen.

  2. Wo sind unsere technischen Schulden, wie viele Zinsen zahlen wir darauf, und wer entscheidet, was abgezahlt wird? Die meisten Teams können das nicht beantworten, was das eigentliche Problem ist, denn Schulden, die Sie nicht sehen können, werden von verwaltet, wer auch immer am lautesten klagt, statt davon, wo die Kosten wirklich liegen. Sie sichtbar zu machen bedeutet, bedeutende Posten zu verfolgen, den Code zu taggen, und Beleg darüber zu sammeln, welche Bereiche Änderungen langsam und scheiteranfällig machen. Der konkurrierende Zug ist, dass jede Stunde auf Abzahlung verbracht eine Stunde nicht auf Features verbracht ist, die Entscheidung muss also eine Portfolio-Entscheidung sein, mit der Führung getroffen, verbindend, wie Sie Ingenieursarbeit regieren (Kapitel 1.5). Bringen Sie Ihre Änderungsscheiterdaten und Ihre Liste der Dateien, die jeder fürchtet anzufassen. Die Antwort sollte sich in eine geschützte, stetige Abzahlungskapazität verwandeln, keine vage Absicht aufzuräumen, wenn sich die Dinge beruhigen.

  3. Welche Teile unserer Codebasis sollten wir absichtlich nicht refaktorieren, und woher würden wir es wissen? Alles zu refaktorieren ist so sehr ein Scheitern wie nichts zu refaktorieren, denn Aufwand, ausgegeben, stabilen, selten berührten, oder bald pensionierten Code aufzuräumen, ist Zinsen, gezahlt auf einen Kredit, den Sie nicht schuldeten. Die Urteilsentscheidung ist echt: ein Modul kann hässlich aussehen und trotzdem der falsche Ort sein zu investieren, wenn niemand es je ändert. Bringen Sie Ihre Änderungshäufigkeitsdaten neben Ihre Komplexitätssignale, denn der Schnittpunkt von hoher Fluktuation und hoher Komplexität ist, wo sich Aufräumung summiert, während Code mit niedriger Fluktuation normalerweise am besten in Ruhe gelassen wird. Das konkurrierende Risiko ist, dass “wir lassen es” zur Ausrede wird, nie etwas Schwieriges anzufassen. Die Antwort sollte Ihnen eine explizite Kurzliste investitionswürdiger Hotspots und Erlaubnis geben, die ruhigen Ecken zu ignorieren.

  4. Vertrauen wir unserer Testsuite tatsächlich genug, um den Code zu refaktorieren, den wir am meisten ändern müssen, und wo müssten wir zuerst Charakterisierungstests schreiben? Ein Sicherheitsnetz, dem Sie nicht vertrauen können, verwandelt Refactoring in Bearbeiten und Hoffen, und bei einem großen Team ist der gruseligste Code normalerweise der am wenigsten getestete Code, was genau ist, wo sich Aufräumung am meisten auszahlen würde. Bringen Sie die Abdeckungs- und Änderungsscheiterdaten für Ihre Hotspots, und seien Sie ehrlich darüber, welche kritischen Module Ihnen keine Warnung geben würden, wenn eine Umstrukturierung Verhalten änderte. Die konkurrierende Erwägung ist, dass Charakterisierungstests für Legacy-Code zu schreiben langsame, unglamouröse Arbeit ist, die kein Feature ausliefert, es ist also leicht, sie für immer aufzuschieben. In Unternehmens- und Behördensystemen unter Prüfung und Änderungskontrolle sind diese festgenagelten Tests auch der Beleg, dass eine Änderung Verhalten bewahrte, sie zu finanzieren ist also gleichzeitig eine Sicherheits- und eine Compliance-Maßnahme; die Antwort sollte benennen, welche Bereiche ein Test-Geschirr bekommen, bevor irgendjemand sie anfasst.

  5. Wenn ein Subsystem wirklich ersetzt werden muss, wie entscheiden wir zwischen einem inkrementellen Würgefeige-Ansatz und einer Umschreibung, und wer hat die Autorität, Nein zur Umschreibung zu sagen? Die Big-Bang-Umschreibung ist die verführerischste und scheiteranfälligste Option auf dem Tisch, denn ein reines Blatt sieht auf dem Papier immer günstiger aus, als mit den alten Beschränkungen zu leben. Für eine große Organisation hält der inkrementelle Pfad (eine Fassade, eine Scheibe nach der anderen, in Produktion verifiziert) das System am Leben und begrenzt Risiko, aber er ist langsamer, verlangt Disziplin zum Beenden, und konkurriert mit dem Appetit auf einen Neuanfang. Bringen Sie die Änderungshäufigkeitskarte des Subsystems, eine ehrliche Schätzung, wie lange eine Umschreibung laufen würde, bevor sie Wert lieferte, und die Verhaltenslücken, die eine parallele Umschreibung schließen müsste. In Behörden- und regulierten Umgebungen ist eine mehrjährige Umschreibung, die am Ende mergt, selten unter Prüfung überlebensfähig, die Antwort sollte also zu Würgefeige oder Zweig durch Abstraktion standardmäßig neigen und jede Umschreibung als Ausnahme behandeln, die mit Beleg argumentiert werden muss.

  6. Wie nutzen wir interne Qualitätskennzahlen, um zu finden, wo sich Schulden konzentrieren, ohne eine Zahl zu einem Ziel werden zu lassen, das Menschen austricksen? Kennzahlen wie Komplexität, Duplikation, Abdeckung, und Änderungsscheiterrate sind der einzige Weg, wie eine große Organisation über Code hinweg sehen kann, den keine einzelne Person liest, doch in dem Moment, in dem eine an ein Tor oder eine Leistungsbeurteilung verdrahtet wird, übernimmt Goodharts Gesetz und die Zahl misst nichts Echtes mehr. Bringen Sie Beispiele, wo eine Kennzahl bereits Verhalten treibt, und fragen Sie, ob sie Gespräche startet oder still Tests belohnt, die nichts behaupten, und Logik, über Funktionen verschmiert, um eine Schwelle auszutricksen. Der konkurrierende Zug ist, dass die Führung eine einfache Dashboard-Zahl will, und “nutzen Sie Urteilsvermögen” ist schwerer zu verkaufen als ein grüner Balken. In Unternehmens- und Behördenkontexten, wo Kennzahlen Governance-Berichterstattung speisen, seien Sie explizit, dass Qualitätssignale Investition informieren und Hotspots lokalisieren, aber nie Einzelpersonen torwächten; die Antwort sollte eine feste Linie zwischen Messen zum Lernen und Messen zum Urteilen ziehen.

Branchenperspektive

Startup. Mit einer Handvoll Ingenieurinnen und keiner Zeit zu verlieren refaktorieren Sie nur opportunistisch: tragen Sie einen Hut pro Commit, damit die Geschichte bisektierbar bleibt, und behalten Sie eine kurze ehrliche Liste der Abkürzungen, die Sie absichtlich nahmen. Starten Sie keine Aufräumkampagnen oder polieren Sie stabile Module; verbringen Sie Ihre knappe Aufmerksamkeit auf die eine Datei, die jeder fürchtet, und schreiben Sie Charakterisierungstests nur dort, wo eine Änderung Sie tatsächlich ängstigt. Absichtliche, sichtbare Schulden sind in diesem Stadium in Ordnung; rücksichtslose unsichtbare Schulden sind, was Sie tötet.

Kleinunternehmen. Ohne dedizierte Plattform- oder Werkzeugspezialistin und mit knappem Budget stützen Sie sich auf das, was Ihre IDE und Ihr Sprachökosystem kostenlos geben: automatisierte Umbenennen- und Extrahieren-Züge, einen Linter, und ein grundlegendes Abdeckungssignal. Behandeln Sie die meisten Schulden als etwas, das Sie im Lauf normaler Arbeit verwalten, statt etwas, wofür Sie eine Beraterin einstellen, um es zu beheben, und bevorzugen Sie den Kauf gut gepflegter Bibliotheken gegenüber dem Bauen und dann-eigenen-Refaktorieren-Müssen. Reservieren Sie den seltenen bezahlten Aufwand für das eine System, dessen Langsamkeit Sie direkt Kundinnen kostet.

Großunternehmen. Über viele Teams hinweg ist das Problem Portfolio-Governance: ein geteiltes Schuldenregister, konsistentes Taggen von Hotspots nach Änderungshäufigkeit und Komplexität, und eine geschützte Scheibe der Kapazität jedes Teams für Abzahlung, damit Aufräumung standardmäßig aufhört, gegen Features zu verlieren. Standardisieren Sie die Zwei-Hüte-Disziplin und Charakterisierungstest-Praxis, damit jede Ingenieurin, die zwischen Teams wechselt, dieselben Regeln findet, und nutzen Sie Würgefeige und Zweig durch Abstraktion für strukturelle Änderung, über Gruppen hinweg koordiniert. Halten Sie Qualitätskennzahlen informativ, damit sie Schulden lokalisieren, ohne in Leistungsbeurteilungen ausgetrickst zu werden.

Behörde. Langlebige Systeme unter strikter Prüfung und Änderungskontrolle machen diszipliniertes Refactoring zu einem Compliance-Vermögenswert, nicht nur einem Ingenieurs-: Umstrukturierung strikt von Verhaltensänderung getrennt zu halten lässt Prüferinnen genau sehen, welche Commits Verhalten änderten und welche nur aufräumten. Beschaffungs- und Transparenzregeln bevorzugen kleine, umkehrbare, beleggestützte Schritte über Big-Bang-Umschreibungen, standardmäßig neigen Sie also zu Würgefeige mit Charakterisierungstests, die dokumentieren, dass Verhalten bewahrt wird. Machen Sie das Schuldenregister und seinen Abzahlungsplan Teil der Wartungsaufzeichnung des Systems, damit Aufsichtsstellen die Rückverfolgbarkeit bekommen, die sie verlangen.

Beispiele

Startup. Ein sechsköpfiges Startup liefert schnell aus und weiß, dass es Schulden aufnimmt, also tut es zwei günstige Dinge gut. Jeder Pull-Request trägt einen Hut: Refactoring-Commits sind getrennt von Feature-Commits, was ihre Geschichte selbst bei hoher Geschwindigkeit bisektierbar hält. Und sie behalten eine kurze, ehrliche Liste der Abkürzungen, die sie absichtlich nahmen, mit einer Ein-Zeilen-Notiz zu den Zinsen, die jede kostet. Wenn ein Zahlungsmodul zur Datei wird, die jeder fürchtet, macht diese Liste plus ihre Änderungsscheitergeschichte den Fall, zwei Tage zu verbringen, um eine sauberere Grenze zu extrahieren. Sie schreiben Charakterisierungstests, um das aktuelle Verhalten festzunageln, refaktorieren darunter mit den Umbenennen- und Extrahieren-Zügen der IDE, und fassen nie die stabilen Module an, die niemand ändert. Die Schulden, die sie tragen, sind absichtlich und sichtbar, sie werden also nie zur rücksichtslosen Art.

Großunternehmen. Ein globales Logistikunternehmen betreibt ein fünfzehn Jahre altes Bestellsystem, das viele Teams jede Woche ändern. Statt einer Umschreibung übernehmen sie das Würgefeige-Muster: eine Fassade sitzt vor dem Monolithen, und eine begrenzte Fähigkeit nach der anderen wird zu neuen Diensten dahinter umgeleitet, in Produktion verifiziert, bevor die nächste Scheibe beginnt. Das über Teams und ein langlebiges System (Kapitel 10.4) zu koordinieren ist der schwierige Teil, also pflegen sie ein geteiltes Schuldenregister, taggen Hotspots nach Änderungshäufigkeit und Komplexität, und reservieren eine feste Scheibe der Kapazität jedes Teams für Abzahlung. Interne Qualitätskennzahlen informieren, wo zu schauen ist, aber torwächten nie die Leistungsbeurteilung irgendjemandes, was die Zahlen ehrlich hält. Über zwei Jahre schrumpft der Monolith stetig und keine einzelne Änderung riskiert je das ganze System.

Behörde. Eine nationale Steuerbehörde muss eine jahrzehntealte Bewertungsplattform unter strikten Prüf- und Änderungskontrollregeln modernisieren, wo jede Codeänderung ein geregeltes, beleggestütztes Ereignis ist. Eine Big-Bang-Umschreibung ist unmöglich, also nutzen sie Zweig durch Abstraktion: eine Abstraktionsschicht wird über die Legacy-Berechnungs-Engine eingeführt, eine neue Implementierung wird dahinter gebaut, und Konsumenten werden eine Steuerregel nach der anderen migriert, jede Migration als kleine, umkehrbare Änderung dokumentiert mit Charakterisierungstests, die beweisen, dass Verhalten unverändert ist. Weil Refactoring strikt von jeder gesetzgeberischen Verhaltensänderung getrennt gehalten wird, können Prüferinnen genau sehen, welche Commits Verhalten änderten und welche nur umstrukturierten. Das Schuldenregister und sein Abzahlungsplan werden Teil der Wartungsaufzeichnung des Systems (Kapitel 3.7), was Aufsichtsstellen die Rückverfolgbarkeit gibt, die sie verlangen.

Geschäftsnutzen: Motivation, ROI und Gesamtbetriebskosten

Die Rendite von Refactoring und Schuldenabzahlung ist die anhaltende Fähigkeit, Software günstig zu ändern, und für die meisten Systeme ist die Mehrheit der Lebenszeitkosten Wartung, hier wird also Gesamtbetriebskosten größtenteils entschieden. Zinsen auf technische Schulden werden in der Währung gezahlt, die die Führung bereits verfolgt: langsamere Lieferung, höhere Änderungsscheiterrate, längere Zeit, sich von Vorfällen zu erholen, und Ingenieurinnen, die den gruseligsten Code meiden. Wenn Sie Schulden sichtbar machen und Abzahlung stetig finanzieren, senken Sie die Kosten jeder zukünftigen Änderung in den Bereichen, die am meisten zählen, und vermeiden das Boom-Bust-Muster, wo vernachlässigte Schulden eine teure Notfallumschreibung erzwingen.

Die Kosten der Übernahme sind bescheiden und größtenteils kulturell: die Zwei-Hüte-Disziplin etablieren, das Sicherheitsnetz bauen, wo Sie refaktorieren müssen, ein Schuldenregister behalten, und eine stetige Scheibe Kapazität für Abzahlung schützen. Die Kosten der Vernachlässigung summieren sich still. Zinsen häufen sich bei jeder Änderung an, bis Geschwindigkeit kollabiert und die Organisation sich erstarrt findet, unfähig, ein System sicher zu modifizieren, das sie nicht mehr versteht, was das teuerste Ergebnis von allen ist. Um den Fall gegenüber der Führung zu machen, verbinden Sie Schulden direkt mit Liefer-Kennzahlen, die ihr bereits wichtig sind, und formulieren Sie Abzahlung als Portfolio-Entscheidung mit messbarem Ertrag, nicht als Ingenieurinnen, die um Zeit zum Aufräumen bitten.

Anti-Muster und Fallstricke

  • Refactoring mit Verhaltensänderung mischen: ein Commit tut beides, ein Bruch kann also nicht zugeordnet werden und Geschichte wird unvertrauenswürdig.
  • Refactoring ohne Sicherheitsnetz: ungetesteten Code umstrukturieren und hoffen, was Bearbeiten durch Glauben ist.
  • Die Big-Bang-Umschreibung: ein funktionierendes System auf einmal ersetzen, ein Muster mit hoher Scheiterrate und langer Zeit bis zum Wert.
  • Refactoring als heldenhaftes Wochenende: ungeprüfte, nicht nachhaltige Aufräumung, die sich selbst rückgängig macht statt stetiger Kapazität.
  • Unsichtbare Schulden: Abkürzungen, die niemand verfolgt, Abzahlung wird also von Beschwerdevolumen statt tatsächlichen Kosten getrieben.
  • Qualitätskennzahlen austricksen: ein Abdeckungs- oder Komplexitätsziel treffen, während echte Qualität sinkt, weil das Maß zum Ziel wurde.
  • Den falschen Code refaktorieren: stabile oder bald pensionierte Module polieren, während die echten Hotspots Sie weiter kosten.
  • Endloses Refactoring: unendliche Umstrukturierung, die nie Wert ausliefert, das Spiegelbild von nie Aufräumen.

Reifegradmodell

  • Stufe 1, Beginnen: Refactoring ist Ad-hoc und reaktiv, oft mit Verhaltensänderung im selben Commit gemischt. Es gibt kein vertrauenswürdiges Sicherheitsnetz, technische Schulden sind unsichtbar und unverfolgt, und Aufräumung geschieht nur in gelegentlichen heldenhaften Ausbrüchen oder gar nicht.
  • Stufe 2, Entwickeln: Manche Teams trennen Refactoring von Verhaltensänderung und stützen sich auf Tests, wo sie existieren, und benannte Refactorings und Charakterisierungstests erscheinen in Taschen. Die Praxis ist über Teams uneinheitlich, Schulden werden diskutiert und manchmal protokolliert, und Abzahlung konkurriert Ad-hoc gegen Features und verliert normalerweise.
  • Stufe 3, Standardisieren: Die Zwei-Hüte-Disziplin, Charakterisierungstests für Legacy-Code, und kleine benannte Refactorings sind dokumentiert und organisationsweit erwartet. Schulden werden in einem geteilten Register verfolgt, das Hauptsumme von Zinsen trennt, und eine geschützte Kapazität für Abzahlung wird jeden Zyklus geplant und in der Prüfung durchgesetzt.
  • Stufe 4, Steuern: Schulden und Aufräumung werden mit Daten gegen Baselines gemessen und gesteuert. Sie verfolgen Änderungshäufigkeit und Komplexität, um Hotspots zu lokalisieren, beobachten Änderungsscheiterrate und Änderungsvorlaufzeit in refaktorierten Bereichen, und zeichnen die Zinsen auf, die jeder bedeutende Posten kostet, sodass Abzahlungsentscheidungen auf Beleg ruhen und Töten-oder-Investieren-Entscheidungen auf Trends statt Beschwerdevolumen getroffen werden. Qualitätssignale informieren Investition, ohne an Tore verdrahtet zu sein, die Menschen austricksen können.
  • Stufe 5, Orchestrieren: Schulden werden als kontinuierlich neu ausbalanciertes Portfolio verwaltet, integriert mit Produkt- und Wartungsplanung über die ganze Organisation. Strukturelle Änderung nutzt routinemäßig Würgefeige und Zweig durch Abstraktion, über Teams koordiniert, Abzahlung ist kontinuierlich und passt zu dem, wo Änderung häufig und schmerzhaft ist, und die Praxis passt sich an, während sich das System und sein Risikobild verschieben, sodass langlebiger Code über Jahrzehnte veränderbar bleibt.

Diskussionsideen

  1. Was ist die tatsächliche, durchgesetzte Regel Ihres Teams, Refactoring von Verhaltensänderung getrennt zu halten, und wo bricht sie unter Termindruck zusammen?
  2. Wie entscheiden Sie mit Beleg, welcher Code Aufräumung verdient und welcher am besten in Ruhe gelassen wird?
  3. Wo würden Charakterisierungstests Sie einen Legacy-Bereich sicher refaktorieren lassen, den Sie derzeit meiden?
  4. Für Ihre nächste größere Modernisierung, wie würde ein Würgefeige-Ansatz aussehen, und welche Fassade oder Abstraktion würden Sie zuerst einführen?
  5. Wer besitzt das technische-Schulden-Register, und wie gewinnt Abzahlung tatsächlich Kapazität gegen Feature-Arbeit?

Wichtigste Erkenntnisse

  • Refactoring bewahrt Verhalten; halten Sie es strikt getrennt von Verhaltensänderung, in separaten Commits.
  • Eine vertrauenswürdige Testsuite ist die Voraussetzung für sicheres Refactoring, und Charakterisierungstests geben Legacy-Code eine.
  • Arbeiten Sie in kleinen, benannten, umkehrbaren Schritten, bevorzugen Sie opportunistische Aufräumung, und nutzen Sie Würgefeige oder Zweig durch Abstraktion für große strukturelle Änderung.
  • Machen Sie technische Schulden sichtbar, trennen Sie Hauptsumme von Zinsen, und finanzieren Sie Abzahlung als stetige Kapazität statt Heldentum.
  • Messen Sie interne Qualität, um Urteil zu leiten, nie als Ziel zum Austricksen, und refaktorieren Sie keinen Code, der stabil oder zur Pensionierung ansteht.

Referenzen und weiterführende Literatur

  • Martin Fowler, Refactoring: Improving the Design of Existing Code, zweite Ausgabe
  • Michael Feathers, Working Effectively with Legacy Code
  • Ward Cunningham, The WyCash Portfolio Management System (OOPSLA-1992-Erfahrungsbericht, Ursprung der Schuldenmetapher)
  • Martin Fowler, “TechnicalDebtQuadrant” und “StranglerFigApplication” (martinfowler.com)
  • Kent Beck, Tidy First? A Personal Exercise in Empirical Software Design
  • Steve McConnell, Code Complete: A Practical Handbook of Software Construction
  • Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship