2.19

View in English

2.19 Refactoring en technische schuld

Overzicht en motivatie

Refactoring is de interne structuur van code veranderen zonder te veranderen wat ze van buiten doet. Je hernoemt een variabele, splitst een lange functie, extraheert een klasse, brengt een kluwen voorwaarden terug tot iets wat een lezer kan volgen, en het programma gedraagt zich precies als voorheen. Dat laatste is de hele discipline. Refactoring is per definitie gedragsbehoudend, en zodra je ook gedrag verandert doe je geen refactoring meer maar twee riskante dingen tegelijk en verberg je de ene achter de andere. Dit hoofdstuk behandelt die twee met opzet als aparte handelingen, omdat de verwarring ertussen is waar de meeste refactoring misgaat.

In een groot team doet dit meer ertoe dan voor een solo-ontwikkelaar, omdat de code die je opruimt code is die honderden anderen lezen, waarvan ze afhangen en die ze bang zijn aan te raken. Refactoring is hoe een gedeelde codebase over jaren en personeelswisselingen bewoonbaar blijft. Het sluit direct aan op softwareconstructie (hoofdstuk 2.9), waar de dagelijkse kwaliteit van coderen wordt bepaald, en op teststrategie (hoofdstuk 2.4), het vangnet dat refactoring überhaupt veilig maakt. Het sluit ook aan op de moeilijkere vraag naar technische schuld: de opgebouwde kosten van snelkoppelingen, verouderende ontwerpen en uitgestelde opruiming die elke toekomstige wijziging trager maken. Refactoring is de belangrijkste manier om die schuld af te lossen, dus de twee onderwerpen horen in één hoofdstuk.

In omgevingen van onderneming en overheid stijgt de inzet. Deze systemen zijn langlevend, vaak tientallen jaren oud en vaak onder audit- en wijzigingsbeheersingsregimes die elke codewijziging als bestuurde gebeurtenis behandelen. Je kunt een uitkeringssysteem voor burgers niet zomaar tijdens een lang weekend herschrijven. Je moderniseert het in kleine, omkeerbare, op bewijs gebaseerde stappen, wat gedisciplineerde refactoring je precies geeft. Dat werk coördineren over veel teams en langlevende systemen (hoofdstuk 10.4) is een van de bepalende uitdagingen van grootschalig engineering, en het fout doen is hoe organisaties vastlopen, niet in staat software te wijzigen die ze niet meer begrijpen.

Kernprincipes

  • Refactoring behoudt gedrag. Als je verandert wat de code doet, is dat een aparte wijziging, apart gedaan.
  • Een betrouwbare testsuite is de voorwaarde voor veilige refactoring, geen optionele extra.
  • Werk in kleine, benoemde, omkeerbare stappen en houd de code na elke stap werkend.
  • Maak technische schuld zichtbaar en volg haar, en financier aflossing als gestage capaciteit in plaats van heldendaden.
  • Refactor de code die je al wijzigt, waar de opruiming haar plek verdient.
  • Niet alle schuld is het aflossen waard. Stabiele, zelden aangeraakte of binnenkort uitgefaseerde code kun je laten liggen.
  • Meet interne kwaliteit om oordeel te informeren, nooit als doel dat gemanipuleerd kan worden.

Aanbevelingen

Houd refactoring en gedragsverandering strikt gescheiden

Besluit voor je begint welke van de twee je doet, en vervaag ze nooit in één commit. Wanneer je refactort, moeten de tests die eerder slaagden daarna ongewijzigd slagen, omdat het waarneembare gedrag niet is verschoven. Wanneer je gedrag verandert, doe dat dan als eigen commit met eigen tests. De reden is praktisch: als een gemengde wijziging iets breekt, kun je niet zien of je herstructurering de bug introduceerde of je gedragsverandering, en in codereview (hoofdstuk 2.5) kan een reviewer over geen van beide helften zuiver redeneren. De gewoonte die werkt is de tweehoedenregel van Martin Fowler: je draagt altijd ofwel de refactoringhoed ofwel de functiehoed, je weet welke, en je wisselt bewust. Aparte commits maken ook de versiebeheergeschiedenis leesbaar, zodat een engineer die een falen bisecteert met vertrouwen de zuivere refactorcommits kan overslaan.

Stel een betrouwbaar vangnet vast voordat je herstructureert

Refactoren zonder tests is gewoon bewerken en hopen. Voordat je iets van belang herstructureert, heb je een suite nodig die je vertrouwt een gedragsverandering te vangen als je er een veroorzaakt, wat het kernargument van teststrategie (hoofdstuk 2.4) is. Voor code die al goede dekking heeft, draai je de tests, refactor je in kleine stappen en draai je ze na elke stap opnieuw. Voor legacycode zonder tests is de eerlijke zet eerst karakteriseringstests te schrijven. Een karakteriseringstest beweert niet wat de code zou moeten doen. Ze legt vast wat de code nu werkelijk doet, inclusief haar eigenaardigheden, zodat elke gedragsverandering als falende test verschijnt. Michael Feathers maakte deze aanpak populair voor precies de situatie waarin grote organisaties leven: code die werkt, ertoe doet en geen tests heeft. Zodra het huidige gedrag is vastgepind, kun je er veilig onder refactoren en pas dan er bovenop gedrag veranderen.

Leer codegeuren herkennen en pas kleine benoemde refactorings toe

Een codegeur is een oppervlakkig teken dat er eronder misschien aandacht nodig is: een functie die te lang is geworden, een klasse die te veel weet, gedupliceerde logica, een lange parameterlijst, namen die liegen over wat ze doen. Een geur is een hint, geen oordeel, dus je onderzoekt in plaats van hem blind te gehoorzamen. De respons is een kleine, benoemde refactoring uit de catalogus van Fowler: Extract Function, Rename Variable, Move Method, Replace Conditional with Polymorphism en tientallen meer. De waarde van benoemde zetten is dat elke klein, begrepen, mechanisch veilig en vaak direct door je IDE ondersteund is. Je componeert grote verbeteringen uit vele kleine betrouwbare stappen, houdt de code de hele tijd groen, in plaats van één grote sprong te maken die je niet kunt verifiëren.

Geef de voorkeur aan opportunistische refactoring en bewaar campagnes voor echte structurele behoefte

De meeste refactoring moet opportunistisch zijn, ingevouwen in het werk dat je al doet. De padvinderregel vat het samen: laat de code iets schoner achter dan je haar aantrof. Wanneer je een bestand aanraakt om een functie toe te voegen of een bug te repareren, begrijp je die hoek al, en kleine opruimingen daar stapelen zich in de tijd op zonder dat iemands toestemming of een apart budget nodig is. Geplande refactoringcampagnes, waarin een team functiewerk stopt om een groot gebied te herstructureren, zijn soms nodig, maar ze zijn duur, moeilijk in te plannen tegen productdruk en riskant als het gebied slecht getest is. Bewaar campagnes voor structurele problemen die opportunistische opruiming niet kan bereiken, en onderbouw de zaak met bewijs over de wijzigingskosten die je betaalt. Geef de voorkeur aan het gestage druppelen van kleine opruimingen. Het is duurzamer dan de incidentele heroïsche herschrijving.

Gebruik het wurgvijgpatroon voor grote structurele verandering

Wanneer een heel subsysteem moet worden vervangen, probeer dan geen big-bang-herschrijving die een jaar loopt en aan het eind samenvoegt. Zo sterven moderniseringsprojecten. Gebruik het wurgvijgpatroon (strangler fig), door Martin Fowler vernoemd naar de liaan die rond een boom groeit en hem geleidelijk vervangt. Je plaatst een façade voor het oude systeem, leidt één plak functionaliteit tegelijk naar nieuwe code achter die façade, verifieert het in productie en herhaalt tot het oude systeem volledig is omringd en kan worden verwijderd. Elke plak is klein, opleverbaar en omkeerbaar, dus het risico blijft begrensd en de waarde komt continu binnen. Een nauwe neef, branch by abstraction, doet hetzelfde binnen één codebase: je introduceert een abstractielaag over het ding dat je wilt vervangen, bouwt de nieuwe implementatie erachter terwijl beide naast elkaar bestaan, schakelt afnemers geleidelijk over en verwijdert de oude implementatie zodra niets er meer van afhangt. Beide laten een legacysysteem evolueren terwijl het in leven blijft, de enige soort modernisering die de meeste grote organisaties zich werkelijk kunnen veroorloven.

Behandel technische schuld als portfolio en maak haar zichtbaar

De schuldmetafoor, gemunt door Ward Cunningham, scheidt twee dingen: de hoofdsom (de rommelige code of snelkoppeling zelf) en de rente (de extra inspanning die elke toekomstige wijziging erdoor betaalt). Niet alle schuld is gelijk. Fowlers kwadrant sorteert haar langs twee assen: bewust tegenover onbewust, en voorzichtig tegenover roekeloos. Voorzichtig-bewuste schuld (“we leveren nu op en ruimen volgende sprint op, en we kennen de kosten”) is een legitieme bedrijfsbeslissing. Roekeloos-onbewuste schuld (“wat is een ontwerppatroon?”) is gewoon schade. De beheertaak, die aansluit op besluitvorming en governance (hoofdstuk 1.5) en haar behandeling van schuld als portfolio, is de schuld zichtbaar te maken zodat erover kan worden geredeneerd: volg significante items waar het werk leeft, tag de code en leg de rente vast die je betaalt, zodat aflossing om capaciteit strijdt op bewijs in plaats van op wie het hardst klaagt. Schuld die je niet kunt zien, kun je niet beheren.

Financier aflossing als gestage capaciteit, niet als heldendaad

Het faalpatroon is opruiming behandelen als iets wat je zult doen “wanneer het rustig wordt”, wat nooit gebeurt. Het duurzame patroon is een vaste, beschermde capaciteit voor aflossing: een expliciete plak van elke cyclus, of een staande afspraak dat opruiming meelift met functiewerk in hetzelfde gebied. Wat niet werkt is de periodieke heroïsche sprint waarin iemand een weekend verbrandt om alles te repareren, omdat die onhoudbaar is, ongekeurd en zichzelf meestal ongedaan maakt. Gestage capaciteit houdt rentebetalingen laag en vermijdt de boom-bustcyclus waarin schuld aangroeit tot een crisis een dure herschrijving afdwingt. Dit is net zozeer een managementverbintenis als een engineeringpraktijk, en het hoort in hoe je softwareonderhoud (hoofdstuk 3.7) plant over de levensduur van een systeem.

Meet interne kwaliteit, maar laat de meting niet het doel worden

Je kunt interne kwaliteit meten met signalen zoals cyclomatische complexiteit (een telling van onafhankelijke paden door een functie), duplicatie, testdekking, faalpercentage van wijzigingen en hoe lang wijzigingen duren in de gebieden die je verdenkt. Deze getallen zijn nuttig om te zien waar schuld zich concentreert en om een trend over de tijd te volgen. Het gevaar is de wet van Goodhart: wanneer een maat een doel wordt, meet ze niets echts meer. Schrijf een dekkingsgetal voor en je krijgt tests die niets beweren. Beloon lage complexiteitsscores en je krijgt logica uitgesmeerd over meer functies om de maat te ontwijken. Gebruik statistieken om gesprekken te beginnen en hotspots te lokaliseren, en koppel een kwaliteitsmaat nooit aan een poort die mensen gemotiveerd zijn te manipuleren.

Weet wanneer je niet moet refactoren

Refactoring is een investering, en sommige code zal haar nooit terugbetalen. Als een module stabiel is, zelden wordt aangeraakt en goed genoeg begrepen om te wijzigen bij de zeldzame gelegenheden dat het moet, is opruimen inspanning besteed aan rente die je niet betaalde. Als code is gepland voor uitfasering, is refactoren polijsten van iets wat je gaat weggooien. De discipline is je opruimbudget te besteden waar verandering frequent en pijnlijk is, waar het verlagen van de rente werkelijk stapelt, en de stille hoeken met rust te laten.

Afwegingen: voor- en nadelen

AanpakVoordelenNadelen
Opportunistische refactoring (padvinderregel)Goedkoop, continu, geen apart budget, stapelt in de tijdOngelijke dekking. Hete bestanden verbeteren terwijl koude wegrotten
Geplande refactoringcampagneLost structurele problemen op die opruiming niet bereiktDuur. Strijdt met functies. Riskant zonder goede tests
Wurgvijg / branch by abstractionIncrementeel, omkeerbaar, houdt het systeem live, begrenst risicoOp papier langzamer dan een herschrijving. Vraagt discipline om af te maken
Big-bang-herschrijvingSchone lei. Geen legacybeperkingenHoog faalpercentage. Lange tijd tot waarde. Gedragsgaten
Bewuste voorzichtige schuldLevert nu waarde. Expliciete, geplande aflossingWordt roekeloos als de aflossing nooit wordt ingepland
Op statistieken gepoorte kwaliteitObjectief, zichtbaar, vangt afdrijving vroegNodigt manipulatie uit. Straft nuance. Kan echte kwaliteit aantasten

De centrale spanning is snelheid nu tegenover veranderbaarheid later, en ze is echt. Een snelkoppeling opleveren kan de juiste keuze zijn wanneer de deadline echt is en de schuld voorzichtig en gevolgd. De fout is te doen alsof de schuld gratis is, of haar onzichtbaar te laten aangroeien tot het systeem te duur is om te wijzigen. Los het op door de ruil elke keer expliciet te maken: benoem de schuld, schat de rente, besluit bewust en leg het besluit vast zodat aflossing kan worden ingepland in plaats van vergeten. Een team dat bewust leent en gestaag aflost blijft jarenlang snel. Een team dat blind leent komt tot stilstand.

Vragen om met je team te bespreken

  1. Hoe voorkomen we dat refactoring en gedragsverandering in dezelfde commit overlopen, en dwingt onze review het werkelijk af? Dit is de fundamentele discipline van het hele hoofdstuk, en degene die onder deadlinedruk het vaakst wordt geschonden, omdat het efficiënt voelt om “dit even op te ruimen nu ik hier toch ben” en alles samen op te leveren. De kosten vallen later: wanneer een gemengde commit productie breekt, kan niemand zien of de herstructurering of de functie het veroorzaakte, en een bisect door je geschiedenis is niet meer betrouwbaar. Neem een handvol recente pull requests mee en controleer eerlijk hoeveel de twee hoeden mengden. De concurrerende overweging is wrijving, want werk splitsen in aparte commits kost vooraf iets meer moeite. Het antwoord moet je commitconventies en je reviewchecklist vormen.

  2. Waar zit onze technische schuld, hoeveel rente betalen we erop en wie beslist wat wordt afgelost? De meeste teams kunnen dit niet beantwoorden, wat het eigenlijke probleem is, omdat schuld die je niet kunt zien wordt beheerd door wie het hardst klaagt in plaats van door waar de kosten echt zitten. Het zichtbaar maken betekent significante items volgen, de code taggen en bewijs verzamelen over welke gebieden wijzigingen traag en faalgevoelig maken. De concurrerende trek is dat elk uur aan aflossing een uur is dat niet aan functies wordt besteed, dus de beslissing moet een portfoliobeslissing zijn, genomen met het leiderschap, aansluitend op hoe je engineeringwerk bestuurt (hoofdstuk 1.5). Neem je gegevens over faalpercentage van wijzigingen en je lijst met bestanden die iedereen vreest aan te raken mee. Het antwoord moet een beschermde, gestage aflossingscapaciteit worden, geen vage intentie op te ruimen wanneer het rustig wordt.

  3. Welke delen van onze codebase moeten we bewust niet refactoren, en hoe zouden we dat weten? Alles refactoren is net zo’n falen als niets refactoren, omdat inspanning besteed aan het opruimen van stabiele, zelden aangeraakte of binnenkort uitgefaseerde code rente is betaald op een lening die je niet schuldig was. Het oordeel is echt: een module kan lelijk ogen en toch de verkeerde plek zijn om in te investeren als niemand haar ooit wijzigt. Neem je gegevens over wijzigingsfrequentie naast je complexiteitssignalen mee, want het snijpunt van hoge churn en hoge complexiteit is waar opruiming stapelt, terwijl code met lage churn meestal het best met rust wordt gelaten. Het concurrerende risico is dat “we laten het liggen” een excuus wordt om nooit iets moeilijks aan te raken. Het antwoord moet je een expliciete shortlist van hotspots geven die investering waard zijn en toestemming om de stille hoeken te negeren.

  4. Vertrouwen we onze testsuite genoeg om de code te refactoren die we het meest moeten wijzigen, en waar zouden we eerst karakteriseringstests moeten schrijven? Een vangnet dat je niet kunt vertrouwen verandert refactoring in bewerken en hopen, en in een groot team is de engste code meestal de minst geteste code, precies waar opruiming het meest zou lonen. Neem de dekkings- en faalpercentagegegevens voor je hotspots mee en wees eerlijk over welke kritieke modules je geen waarschuwing zouden geven als een herstructurering gedrag veranderde. De concurrerende overweging is dat karakteriseringstests schrijven voor legacycode langzaam, onglamoureus werk is dat geen functie oplevert, dus het is makkelijk het eindeloos uit te stellen. In systemen van onderneming en overheid onder audit en wijzigingsbeheersing zijn die vastgepinde tests ook het bewijs dat een wijziging gedrag behield, dus ze financieren is tegelijk een veiligheids- en een compliancemaatregel. Het antwoord moet benoemen welke gebieden een testharnas krijgen voordat iemand ze aanraakt.

  5. Wanneer een subsysteem werkelijk moet worden vervangen, hoe kiezen we tussen een incrementele wurgvijgaanpak en een herschrijving, en wie heeft de bevoegdheid nee te zeggen tegen de herschrijving? De big-bang-herschrijving is de meest verleidelijke en meest faalgevoelige optie op tafel, omdat een schone lei op papier altijd goedkoper lijkt dan leven met de oude beperkingen. Voor een grote organisatie houdt het incrementele pad (een façade, één plak tegelijk, geverifieerd in productie) het systeem in leven en begrenst het risico, maar het is langzamer, vraagt discipline om af te maken en strijdt met de eetlust naar een frisse start. Neem de wijzigingsfrequentiekaart van het subsysteem mee, een eerlijke schatting van hoe lang een herschrijving zou lopen voordat ze waarde levert en de gedragsgaten die een parallelle herschrijving zou moeten dichten. In overheids- en gereguleerde settings is een herschrijving van meerdere jaren die aan het eind samenvoegt zelden te overleven onder audit, dus het antwoord moet standaard wurgvijg of branch by abstraction zijn en elke herschrijving behandelen als een uitzondering die met bewijs moet worden bepleit.

  6. Hoe gebruiken we interne kwaliteitsstatistieken om te vinden waar schuld zich concentreert zonder dat een getal een doel wordt dat mensen manipuleren? Statistieken als complexiteit, duplicatie, dekking en faalpercentage van wijzigingen zijn de enige manier waarop een grote organisatie kan zien over code die geen enkel persoon leest, maar zodra een ervan aan een poort of een functioneringsgesprek wordt gekoppeld, neemt de wet van Goodhart het over en meet het getal niets echts meer. Neem voorbeelden mee van waar een statistiek al gedrag stuurt en vraag of ze gesprekken begint of stilletjes tests beloont die niets beweren en logica uitgesmeerd over functies om een drempel te ontwijken. De concurrerende trek is dat het leiderschap een eenvoudig dashboardgetal wil, en “gebruik oordeel” is moeilijker te verkopen dan een groene balk. Wees in contexten van onderneming en overheid, waar statistieken governance-rapportage voeden, expliciet dat kwaliteitssignalen investering informeren en hotspots lokaliseren maar nooit individuen poorten. Het antwoord moet een harde lijn trekken tussen meten om te leren en meten om te beoordelen.

Sectorperspectief

Startup. Met een handvol engineers en geen runway om te verspillen refactor je alleen opportunistisch: draag één hoed per commit zodat de geschiedenis bisecteerbaar blijft en houd een korte eerlijke lijst bij van de snelkoppelingen die je bewust nam. Start geen opruimcampagnes en polijst geen stabiele modules. Besteed je schaarse aandacht aan het ene bestand dat iedereen vreest, en schrijf karakteriseringstests alleen waar een wijziging je werkelijk beangstigt. Bewuste, zichtbare schuld is in dit stadium prima. Roekeloze onzichtbare schuld is wat je doodt.

Kleinbedrijf. Zonder aparte platform- of toolingspecialist en met een krap budget leun je op wat je IDE en taalecosysteem gratis geven: geautomatiseerde rename- en extractzetten, een linter en een basaal dekkingssignaal. Behandel de meeste schuld als iets wat je in de loop van het normale werk beheert in plaats van iets wat je een consultant inhuurt om te repareren, en geef de voorkeur aan het kopen van goed onderhouden bibliotheken boven bouwen en dan je eigen refactoren. Bewaar de zeldzame betaalde inspanning voor het ene systeem waarvan de traagheid je direct klanten kost.

Grote onderneming. Over veel teams is het probleem portfoliogovernance: een gedeeld schuldregister, consistente tagging van hotspots naar wijzigingsfrequentie en complexiteit en een beschermde plak van de capaciteit van elk team voor aflossing zodat opruiming niet standaard van functies verliest. Standaardiseer de tweehoedendiscipline en karakteriseringstestpraktijk zodat elke engineer die tussen teams beweegt dezelfde regels vindt, en gebruik wurgvijg en branch by abstraction voor structurele verandering gecoördineerd over groepen. Houd kwaliteitsstatistieken informatief zodat ze schuld lokaliseren zonder in functioneringsgesprekken te worden gemanipuleerd.

Overheid. Langlevende systemen onder strikte audit en wijzigingsbeheersing maken gedisciplineerde refactoring tot een complianceactivum, niet alleen een engineeringactivum: herstructurering strikt gescheiden houden van gedragsverandering laat auditors precies zien welke commits gedrag veranderden en welke alleen opruimden. Aanbestedings- en transparantieregels geven de voorkeur aan kleine, omkeerbare, op bewijs gebaseerde stappen boven big-bang-herschrijvingen, dus kies standaard voor wurgvijg met karakteriseringstests die documenteren dat gedrag behouden blijft. Maak het schuldregister en zijn aflossingsplan onderdeel van het onderhoudsdossier van het systeem zodat toezichthoudende organen de traceerbaarheid krijgen die ze eisen.

Voorbeelden

Startup. Een startup van zes personen levert snel op en weet dat ze schuld op zich neemt, dus doet ze twee goedkope dingen goed. Elke pull request draagt één hoed: refactorcommits staan los van functiecommits, wat hun geschiedenis bisecteerbaar houdt zelfs bij hoge snelheid. En ze houden een korte, eerlijke lijst bij van de snelkoppelingen die ze bewust namen, met een notitie van één regel over de rente die elk kost. Wanneer een betaalmodule het bestand wordt dat iedereen vreest, maakt die lijst plus hun geschiedenis van faalpercentage de zaak om twee dagen te besteden aan het extraheren van een schonere grens. Ze schrijven karakteriseringstests om het huidige gedrag vast te pinnen, refactoren eronder met de rename- en extractzetten van de IDE en raken de stabiele modules die niemand wijzigt nooit aan. De schuld die ze dragen is bewust en zichtbaar, dus ze verandert nooit in de roekeloze soort.

Grote onderneming. Een wereldwijd logistiek bedrijf draait een vijftien jaar oud ordersysteem dat veel teams elke week wijzigen. In plaats van een herschrijving nemen ze het wurgvijgpatroon aan: een façade staat voor de monoliet, en één afgebakende capaciteit tegelijk wordt omgeleid naar nieuwe services erachter, in productie geverifieerd voordat de volgende plak begint. Dit coördineren over teams en een langlevend systeem (hoofdstuk 10.4) is het moeilijke deel, dus ze onderhouden een gedeeld schuldregister, taggen hotspots naar wijzigingsfrequentie en complexiteit en reserveren een vaste plak van de capaciteit van elk team voor aflossing. Interne kwaliteitsstatistieken informeren waar te kijken maar poorten nooit iemands functioneringsgesprek, wat de getallen eerlijk houdt. Over twee jaar krimpt de monoliet gestaag en riskeert geen enkele wijziging ooit het hele systeem.

Overheid. Een nationale belastingdienst moet een tientallen jaren oud beoordelingsplatform moderniseren onder strikte audit- en wijzigingsbeheersingsregels, waar elke codewijziging een bestuurde, op bewijs gebaseerde gebeurtenis is. Een big-bang-herschrijving is onmogelijk, dus gebruiken ze branch by abstraction: een abstractielaag wordt geïntroduceerd over de legacyberekeningsengine, een nieuwe implementatie wordt erachter gebouwd en afnemers worden één belastingregel tegelijk gemigreerd, elke migratie gedocumenteerd als kleine, omkeerbare wijziging met karakteriseringstests die bewijzen dat gedrag ongewijzigd is. Omdat refactoring strikt gescheiden wordt gehouden van elke wetgevende gedragsverandering, kunnen auditors precies zien welke commits gedrag veranderden en welke alleen herstructureerden. Het schuldregister en zijn aflossingsplan worden onderdeel van het onderhoudsdossier van het systeem (hoofdstuk 3.7), wat toezichthoudende organen de traceerbaarheid geeft die ze eisen.

Zakelijke onderbouwing: motivatie, ROI en TCO

Het rendement van refactoring en schuldaflossing is het blijvende vermogen software goedkoop te wijzigen, en voor de meeste systemen is het grootste deel van de levensduurkosten onderhoud, dus hier wordt de total cost of ownership grotendeels bepaald. Rente op technische schuld wordt betaald in de valuta die het bestuur al volgt: tragere oplevering, hoger faalpercentage van wijzigingen, langere tijd om van incidenten te herstellen en engineers die de engste code mijden. Wanneer je schuld zichtbaar maakt en aflossing gestaag financiert, verlaag je de kosten van elke toekomstige wijziging in de gebieden die ertoe doen en vermijd je het boom-bustpatroon waarin verwaarloosde schuld een dure noodherschrijving afdwingt.

De invoeringskosten zijn bescheiden en vooral cultureel: de tweehoedendiscipline vaststellen, het vangnet bouwen waar je moet refactoren, een schuldregister bijhouden en een gestage plak capaciteit beschermen voor aflossing. De kosten van verwaarlozing stapelen zich stilletjes op. Rente loopt op bij elke wijziging tot de snelheid instort en de organisatie zich bevroren vindt, niet in staat een systeem veilig te wijzigen dat ze niet meer begrijpt, de duurste uitkomst van allemaal. Verbind schuld om het bestuur te overtuigen direct aan leveringsstatistieken waar ze al om geven, en formuleer aflossing als portfoliobeslissing met meetbare opbrengst, niet als engineers die om tijd vragen om op te ruimen.

Antipatronen en valkuilen

  • Refactoring mengen met gedragsverandering: één commit doet beide, zodat een breuk niet kan worden toegeschreven en de geschiedenis onbetrouwbaar wordt.
  • Refactoren zonder vangnet: ongeteste code herstructureren en hopen, wat bewerken uit geloof is.
  • De big-bang-herschrijving: een werkend systeem in één keer vervangen, een patroon met hoog faalpercentage en lange tijd tot waarde.
  • Refactoring als heroïsch weekend: ongekeurde, onhoudbare opruiming die zichzelf ongedaan maakt in plaats van gestage capaciteit.
  • Onzichtbare schuld: snelkoppelingen die niemand volgt, zodat aflossing wordt gedreven door klaagvolume in plaats van werkelijke kosten.
  • Kwaliteitsstatistieken manipuleren: een dekkings- of complexiteitsdoel halen terwijl echte kwaliteit daalt, omdat de maat het doel werd.
  • De verkeerde code refactoren: stabiele of binnenkort uitgefaseerde modules polijsten terwijl de echte hotspots blijven kosten.
  • Eeuwigdurende refactoring: eindeloze herstructurering die nooit waarde oplevert, het spiegelbeeld van nooit opruimen.

Volwassenheidsmodel

  • Niveau 1, Initiëren: Refactoring is ad hoc en reactief, vaak gemengd met gedragsverandering in dezelfde commit. Er is geen betrouwbaar vangnet, technische schuld is onzichtbaar en niet gevolgd, en opruiming gebeurt alleen in incidentele heroïsche uitbarstingen of helemaal niet.
  • Niveau 2, Ontwikkelen: Sommige teams scheiden refactoring van gedragsverandering en leunen op tests waar ze bestaan, en benoemde refactorings en karakteriseringstests verschijnen in zakken. De praktijk is inconsistent over teams, schuld wordt besproken en soms gelogd, en aflossing strijdt ad hoc tegen functies en verliest meestal.
  • Niveau 3, Standaardiseren: De tweehoedendiscipline, karakteriseringstests voor legacycode en kleine benoemde refactorings zijn gedocumenteerd en organisatiebreed verwacht. Schuld wordt gevolgd in een gedeeld register dat hoofdsom van rente scheidt, en een beschermde capaciteit voor aflossing wordt elke cyclus gepland en in review afgedwongen.
  • Niveau 4, Beheersen: Schuld en opruiming worden gemeten en gestuurd met data aan de hand van uitgangswaarden. Je volgt wijzigingsfrequentie en complexiteit om hotspots te lokaliseren, let op faalpercentage van wijzigingen en doorlooptijd van wijzigingen in gerefactorde gebieden en legt de rente vast die elk significant item kost, zodat aflossingsbeslissingen op bewijs rusten en beslissingen om te schrappen of te investeren worden genomen op trends in plaats van klaagvolume. Kwaliteitssignalen informeren investering zonder aan poorten te worden gekoppeld die mensen kunnen manipuleren.
  • Niveau 5, Orkestreren: Schuld wordt beheerd als continu herbalanceerd portfolio geïntegreerd met product- en onderhoudsplanning over de hele organisatie. Structurele verandering gebruikt routinematig wurgvijg en branch by abstraction gecoördineerd over teams, aflossing is continu en afgestemd op waar verandering frequent en pijnlijk is, en de praktijk past zich aan naarmate het systeem en zijn risicobeeld verschuiven, zodat langlevende code decennialang veranderbaar blijft.

Ideeën voor discussie

  1. Wat is de werkelijke, afgedwongen regel van je team om refactoring gescheiden te houden van gedragsverandering, en waar breekt ze onder deadlinedruk?
  2. Hoe besluit je, met bewijs, welke code opruiming verdient en welke het best met rust wordt gelaten?
  3. Waar zouden karakteriseringstests je veilig een legacygebied laten refactoren dat je nu mijdt?
  4. Hoe zou een wurgvijgaanpak eruitzien voor je volgende grote modernisering, en welke façade of abstractie zou je als eerste introduceren?
  5. Wie bezit het register van technische schuld, en hoe wint aflossing werkelijk capaciteit van functiewerk?

Belangrijkste inzichten

  • Refactoring behoudt gedrag. Houd haar strikt gescheiden van gedragsverandering, in aparte commits.
  • Een betrouwbare testsuite is de voorwaarde voor veilige refactoring, en karakteriseringstests geven legacycode er een.
  • Werk in kleine, benoemde, omkeerbare stappen, geef de voorkeur aan opportunistische opruiming en gebruik wurgvijg of branch by abstraction voor grote structurele verandering.
  • Maak technische schuld zichtbaar, scheid hoofdsom van rente en financier aflossing als gestage capaciteit in plaats van heldendaden.
  • Meet interne kwaliteit om oordeel te sturen, nooit als doel om te manipuleren, en refactor geen code die stabiel is of is gepland voor uitfasering.

Referenties en verder lezen

  • Martin Fowler, Refactoring: Improving the Design of Existing Code, second edition
  • Michael Feathers, Working Effectively with Legacy Code
  • Ward Cunningham, The WyCash Portfolio Management System (OOPSLA 1992 experience report, origin of the debt metaphor)
  • Martin Fowler, “TechnicalDebtQuadrant” and “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