Artikel Refaktorering · Verifiering

Refaktorera med AI utan att ändra beteendet

Du ber modellen göra en svårläst funktion enklare. Den delar upp den, byter namn och tar bort upprepning — men passar samtidigt på att normalisera tomma värden, kasta ett annat fel och sortera resultatet. Koden blev renare. Produkten blev annorlunda.

Senast granskad: 1 aug 2026

En mörk glasram med oförändrade gröna anslutningspunkter omsluter metallmoduler som ordnas om, medan en abstrakt primatprofil av neurala noder lutar sig in från vänster.
Insidan får byggas om; gränsen mot omvärlden är refaktoreringens fasta kontrakt.

Kort sagt

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.

”Alla tester är gröna” räcker inte

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.

Returvärden
Vilka värden och typer kommer tillbaka för normala fall, tomma indata och gränsvärden? Spelar ordning, precision eller formatering roll?
Fel
Vilka fel kastas eller returneras, vid vilken tidpunkt och med vilken information? Ett ”bättre” fel kan fortfarande bryta en anropare.
Sidoeffekter
Vilka skrivningar, nätverksanrop, händelser och loggar sker — och i vilken ordning? Kontrollera också vad som uttryckligen inte ska ske.
Kompatibilitet
Vilka publika namn, parametrar, standardvärden och dataformat använder annan kod? Interna namn är fria; gränssnitt som redan används är det inte.

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

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.

1
Frys nuläget. Kör den befintliga sviten och lägg till karakteriseringstesterna. Spara ett rent utgångsläge. Om något redan är rött ska det dokumenteras innan refaktoreringen börjar.
2
Be om en plan utan kod. Låt modellen lista föreslagna steg, vilka filer varje steg rör och vilket observerbart beteende som kan påverkas. Ta bort allt som inte behövs för målet.
3
Gör en mekanisk ändring. Flytta en funktion, byt ett internt namn eller extrahera ett block — inte alla tre. Förbjud ändringar i publika gränssnitt, felbeteende och dataformat.
4
Kör och jämför. Kör tester, typkontroll och statisk analys. Inspektera diffen efter ändringar som inte behövdes för steget. Återställ sidoförbättringar även om de ser bra ut.
5
Spara steget separat. Varje godkänt steg ska kunna återställas utan att nästa steg följer med. Fortsätt först när diffen går att förklara rad för rad.

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.

Tomt blir saknat
Tom sträng, null och frånvarande värde slås ihop genom en kortare sanningskontroll. Anropare som använder skillnaden får ett nytt resultat.
Ordningen blir ”stabil”
Resultat sorteras för att bli förutsägbara, trots att insättningsordning eller prioritering tidigare var en del av beteendet.
Fel städas upp
Flera feltyper ersätts av en gemensam typ, feltexten ändras eller ett undantag fångas tidigare än förr.
Standardvärden flyttas
Ett standardvärde appliceras tidigare och kan inte längre skiljas från ett värde som anroparen skickade uttryckligen.
Arbete parallelliseras
Oberoende anrop körs samtidigt för att förenkla eller snabba upp, men ordning, belastning och partiella fel förändras.
”Död” kod försvinner
En gren ser oanvänd ut i den kontext modellen fick. Dynamiska anrop, konfiguration eller externa konsumenter kan ändå nå den.

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.

Prompt för säker refaktorering Ett steg

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.

Stoppsignaler

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.

Läs vidare
Testning av AI-genererad kod: mer test, inte mindre