Refaktorering betyder att strukturen ändras medan det observerbara beteendet består. Innan modellen får röra implementationen behöver du därför skriva ned vad som ska förbli sant och fånga det i körbara kontroller. Sedan ändras en strukturell sak åt gången. Ett grönt test är bevis först när testet faktiskt låser rätt beteende.
Modellen ser förbättringar där du ser ett kontrakt
En människa som har arbetat med systemet länge vet att en märklig detalj kan vara avsiktlig. Listan sorteras inte eftersom anropsordningen har betydelse. Ett tomt värde skickas vidare eftersom nästa lager skiljer det från ett saknat värde. Ett visst undantag fångas av en klient som inte kan uppdateras samtidigt.
För modellen ser samma detaljer ofta ut som inkonsekvenser. Den har tränats på mängder av mönster för hur kod brukar skrivas, och kan därför föreslå en mer idiomatisk implementation. Men den kan inte se vilka avvikelser som är misstag och vilka som är en del av systemets verkliga kontrakt om du inte visar det.
Där ligger den särskilda risken med AI-refaktorering. Modellen producerar inte bara den flytt eller namnändring du bad om. Den fyller gärna i luckor, jämnar ut skillnader och gör närliggande förbättringar. Var och en kan se rimlig ut i diffen. Tillsammans gör de refaktoreringen omöjlig att skilja från en funktionsändring.
Befintliga tester visar bara det de råkar kontrollera. Om sorteringsordning, feltyp eller sidoeffekt saknar test kan implementationen ändras utan en enda röd rad. Börja därför med att fråga vilket beteende som måste låsas — inte hur stor testsviten redan är.
Skriv kontraktet som en lista över observationer
”Samma beteende” är för vagt för både människa och modell. Gör formuleringen konkret genom att beskriva vad en anropare kan observera. Du behöver inte dokumentera varje intern detalj; just det interna ska få ändras. Du behöver fånga gränsen mellan koden och resten av systemet.
Listan är beteendekontraktet för just den här ändringen. Den kan vara kort. Värdet ligger i att den tvingar fram ett beslut om vad som är heligt och vad som får förbättras. Om du upptäcker ett beteende som faktiskt borde ändras, skriv det som en separat uppgift. Då får det egna krav, tester och granskning i stället för att smugglas in i en strukturell diff.
Karakteriseringstester låser det systemet gör i dag
Ett karakteriseringstest beskriver befintligt beteende utan att påstå att beteendet är idealiskt. Du kör den gamla implementationen med representativa indata, observerar resultatet och gör observationen körbar. Testet blir ett staket runt refaktoreringen.
Det skiljer sig från att be modellen generera tester efter omskrivningen. Då kan samma missförstånd hamna på båda sidor: modellen skriver ny kod och tester som bekräftar den nya kodens antaganden. Test och implementation är överens, men båda har lämnat det gamla kontraktet.
När fallen är för många för att räknas upp för hand kan du jämföra implementationerna direkt i stället. Behåll den gamla funktionen under omskrivningen, kör båda mot samma indata — inspelade anrop, ett urval produktionsliknande data eller genererade värden — och låt testet kräva identiskt resultat. Skillnaderna som dyker upp visar var kontraktet i praktiken går. Den gamla vägen tas bort först när jämförelsen är grön.
Välj fall som avslöjar semantiska skillnader
- Ett normalt fall som visar den vanligaste vägen genom koden.
- Tomt, saknat och uttryckligen null-liknande värde om systemet skiljer dem åt.
- Första och sista giltiga gränsvärdet, plus ett värde precis utanför.
- Två element i en ordning som en automatisk sortering skulle ändra.
- Ett fel där både feltyp och kvarvarande sidoeffekter kontrolleras.
Testerna ska helst gå genom det publika gränssnittet. Om de låser privata funktioner och intern anropsordning gör de den gamla strukturen till kontrakt och motarbetar själva refaktoreringen. Testa vad omvärlden ser. Använd smalare enhetstester bara där ett externt test blir för långsamt eller opålitligt.
Arbetsflödet: en strukturell förändring per diff
När kontraktet och säkerhetsnätet finns kan modellen vara mycket effektiv. Den är bra på mekaniska flyttar, namnbyten, extrahering av funktioner och borttagning av upprepning. Men ge den ett smalt förändringsutrymme och ett tydligt stoppvillkor.
Det här är samma skäl till att små diffar vinner, men med ett snävare facit: refaktoreringsdiffen ska förändra kodens form och lämna dess utsida ifred. Om du inte kan beskriva vilken intern struktur som ändras har steget förmodligen blivit för brett.
Sex förändringar som gärna smyger med
Vissa beteendeskiften återkommer eftersom de ser ut som självklara förbättringar. Sök aktivt efter dem i diffen i stället för att vänta på att ett test ska råka hitta dem.
En bra granskning frågar därför inte bara om den nya koden fungerar. Den frågar varför varje borttagen gren, flyttad kontroll och ändrad standard var nödvändig för refaktoreringen. Kodgranskningsguidens sju frågor gäller fortfarande; beteendekontraktet gör dem specifika nog för just den här diffen.
Prompten som håller isär struktur och beteende
Ge modellen kontraktet, den tillåtna förändringen och stoppregeln i samma prompt. Be den uttryckligen rapportera frestande förbättringar utan att genomföra dem. Då kan en verklig funktionsändring bli en senare, medveten uppgift.
Refaktorera [fil, modul eller funktion] genom att endast [extrahera funktion / flytta kod / byta interna namn / ta bort angiven upprepning].
Följande observerbara beteende ska vara oförändrat: [returvärden, ordning, fel, sidoeffekter, publika namn och gränsfall].
Ändra inte publika gränssnitt, dataformat, standardvärden, feltyper, loggning eller exekveringsordning. Lägg inte till andra förbättringar.
Före kod: beskriv den minsta planen och peka ut var beteendet riskerar att ändras. Gör sedan bara det första steget.
Efter kod: sammanfatta varje ändrad radgrupp och ange vilket test som bevisar att kontraktet består. Om ett beteende saknar test, stoppa och föreslå testet i stället för att anta.
Sista meningen är viktigast. Den gör frånvaron av bevis till ett stoppvillkor. Utan den behandlar modellen lätt ett grönt testresultat som generell trygghet, även när just den ändrade grenen aldrig kördes.
När du ska stoppa refaktoreringen
Ibland går kontraktet inte att skriva med rimlig säkerhet. Koden kan bero på timing, globala tillstånd, odokumenterade konsumenter eller produktionsdata som inte finns i testmiljön. Då är lösningen inte en mer detaljerad prompt. Lösningen är att först kartlägga systemet.
Pausa när ingen kan förklara vilka externa anropare som finns, när testerna kräver att interna detaljer låses, när samtidighet eller transaktioner ändras, eller när diffen blandar refaktorering med buggrättning. Använd arbetsflödet för en ärvd kodbas för att kartlägga ingångar och dataflöden innan du fortsätter.
Det kan också vara rätt att behålla ful kod. En liten, stabil modul med högt ändringspris och lågt underhållsbehov blir inte automatiskt bättre av en elegantare form. Refaktorering är värdefull när den gör kommande arbete säkrare eller enklare — inte när den bara gör modellen nöjd med mönstret.
Facit ligger utanför implementationen
AI gör den mekaniska delen av refaktorering billig. Det gör avgränsningen mer, inte mindre, viktig. När omskrivningen tar sekunder är det lätt att acceptera en större förändring än du hade orkat göra för hand, och lika lätt att missa att den nya koden besvarar en något annan fråga.
Beteendekontraktet flyttar facit ut ur implementationen. Karakteriseringstesterna gör det körbart. De små diffarna gör varje avvikelse möjlig att hitta och återställa. Först då får modellen den uppgift den faktiskt är bra på: att ändra kodens form inom gränser som en människa har valt och fortfarande äger.