Norsk

En praktisk veiledning for refaktorering av eldre kode, som dekker identifisering, prioritering, teknikker og beste praksis for modernisering og vedlikehold.

Slik temmer du udyret: Refaktoreringstrategier for eldre kode

Eldre kode. Selve begrepet fremkaller ofte bilder av sprikende, udokumenterte systemer, skjøre avhengigheter og en overveldende følelse av frykt. Mange utviklere over hele verden står overfor utfordringen med å vedlikeholde og utvikle disse systemene, som ofte er kritiske for forretningsdriften. Denne omfattende guiden gir praktiske strategier for refaktorering av eldre kode, og gjør en kilde til frustrasjon om til en mulighet for modernisering og forbedring.

Hva er eldre kode?

Før vi dykker ned i refaktoreringsteknikker, er det viktig å definere hva vi mener med "eldre kode". Selv om begrepet rett og slett kan referere til eldre kode, fokuserer en mer nyansert definisjon på vedlikeholdbarheten. Michael Feathers, i sin banebrytende bok "Working Effectively with Legacy Code", definerer eldre kode som kode uten tester. Denne mangelen på tester gjør det vanskelig å trygt modifisere koden uten å introdusere regresjoner. Eldre kode kan imidlertid også ha andre kjennetegn:

Det er viktig å merke seg at eldre kode ikke er iboende dårlig. Den representerer ofte en betydelig investering og inneholder verdifull domenekunnskap. Målet med refaktorering er å bevare denne verdien samtidig som man forbedrer kodens vedlikeholdbarhet, pålitelighet og ytelse.

Hvorfor refaktorere eldre kode?

Refaktorering av eldre kode kan være en skremmende oppgave, men fordelene veier ofte opp for utfordringene. Her er noen sentrale grunner til å investere i refaktorering:

Identifisering av refaktoreringkandidater

Ikke all eldre kode trenger å refaktoreres. Det er viktig å prioritere refaktoreringstiltak basert på følgende faktorer:

Eksempel: Se for deg et globalt logistikkselskap med et eldre system for håndtering av forsendelser. Modulen som er ansvarlig for å beregne fraktkostnader oppdateres ofte på grunn av endrede regelverk og drivstoffpriser. Denne modulen er en førsteklasses kandidat for refaktorering.

Refaktoreringsteknikker

Det finnes en rekke refaktoreringsteknikker, hver utformet for å adressere spesifikke kodelukter eller forbedre spesifikke aspekter av koden. Her er noen vanlige teknikker:

Komponering av metoder

Disse teknikkene fokuserer på å bryte ned store, komplekse metoder til mindre, mer håndterbare metoder. Dette forbedrer lesbarheten, reduserer duplisering og gjør koden enklere å teste.

Flytting av funksjoner mellom objekter

Disse teknikkene fokuserer på å forbedre designet av klasser og objekter ved å flytte ansvarsområder dit de hører hjemme.

Organisering av data

Disse teknikkene fokuserer på å forbedre måten data lagres og aksesseres på, noe som gjør det enklere å forstå og endre.

Forenkling av betingede uttrykk

Betinget logikk kan raskt bli komplisert. Disse teknikkene tar sikte på å klargjøre og forenkle.

Forenkling av metodekall

Håndtering av generalisering

Dette er bare noen få eksempler på de mange refaktoreringsteknikkene som er tilgjengelige. Valget av hvilken teknikk man skal bruke, avhenger av den spesifikke kodelukten og det ønskede resultatet.

Eksempel: En stor metode i en Java-applikasjon brukt av en global bank beregner rentesatser. Ved å bruke Extract Method for å lage mindre, mer fokuserte metoder, forbedres lesbarheten, og det blir enklere å oppdatere rentesatsberegningslogikken uten å påvirke andre deler av metoden.

Refaktoreringprosessen

Refaktorering bør tilnærmes systematisk for å minimere risiko og maksimere sjansene for suksess. Her er en anbefalt prosess:

  1. Identifiser refaktoreringkandidater: Bruk kriteriene nevnt tidligere for å identifisere områder av koden som vil ha størst nytte av refaktorering.
  2. Lag tester: Før du gjør noen endringer, skriv automatiserte tester for å verifisere den eksisterende atferden til koden. Dette er avgjørende for å sikre at refaktorering ikke introduserer regresjoner. Verktøy som JUnit (Java), pytest (Python) eller Jest (JavaScript) kan brukes til å skrive enhetstester.
  3. Refaktorer inkrementelt: Gjør små, inkrementelle endringer og kjør testene etter hver endring. Dette gjør det lettere å identifisere og fikse eventuelle feil som introduseres.
  4. Commit ofte: Commit endringene dine til versjonskontroll ofte. Dette gjør at du enkelt kan gå tilbake til en tidligere versjon hvis noe går galt.
  5. Gjennomgå koden: Få koden din gjennomgått av en annen utvikler. Dette kan bidra til å identifisere potensielle problemer og sikre at refaktoreringen gjøres riktig.
  6. Overvåk ytelsen: Etter refaktorering, overvåk ytelsen til systemet for å sikre at endringene ikke har introdusert noen ytelsesregresjoner.

Eksempel: Et team som refaktorerer en Python-modul i en global e-handelsplattform bruker `pytest` for å lage enhetstester for den eksisterende funksjonaliteten. Deretter bruker de Extract Class-refaktoreringen for å skille ansvarsområder og forbedre modulens struktur. Etter hver liten endring kjører de testene for å sikre at funksjonaliteten forblir uendret.

Strategier for å introdusere tester i eldre kode

Som Michael Feathers så treffende sa, er eldre kode kode uten tester. Å introdusere tester i eksisterende kodebaser kan føles som en massiv oppgave, men det er avgjørende for trygg refaktorering. Her er flere strategier for å nærme seg denne oppgaven:

Karakteriseringstester (også kjent som Golden Master-tester)

Når du har å gjøre med kode som er vanskelig å forstå, kan karakteriseringstester hjelpe deg med å fange opp dens eksisterende atferd før du begynner å gjøre endringer. Ideen er å skrive tester som bekrefter den nåværende utdataen fra koden for et gitt sett med inndata. Disse testene verifiserer ikke nødvendigvis korrekthet; de dokumenterer bare hva koden *for øyeblikket* gjør.

Fremgangsmåte:

  1. Identifiser en kodeenhet du vil karakterisere (f.eks. en funksjon eller metode).
  2. Lag et sett med inndataverdier som representerer en rekke vanlige og ekstreme scenarier.
  3. Kjør koden med disse inndataene og fang opp de resulterende utdataene.
  4. Skriv tester som bekrefter at koden produserer nøyaktig disse utdataene for disse inndataene.

Advarsel: Karakteriseringstester kan være skjøre hvis den underliggende logikken er kompleks eller dataavhengig. Vær forberedt på å oppdatere dem hvis du må endre kodens atferd senere.

Sprout-metode og Sprout-klasse

Disse teknikkene, også beskrevet av Michael Feathers, tar sikte på å introdusere ny funksjonalitet i et eldre system samtidig som risikoen for å ødelegge eksisterende kode minimeres.

Sprout-metode: Når du trenger å legge til en ny funksjon som krever endring av en eksisterende metode, lag en ny metode som inneholder den nye logikken. Kall deretter denne nye metoden fra den eksisterende metoden. Dette lar deg isolere den nye koden og teste den uavhengig.

Sprout-klasse: Ligner på Sprout-metode, men for klasser. Opprett en ny klasse som implementerer den nye funksjonaliteten, og integrer den deretter i det eksisterende systemet.

Sandkasse-testing (Sandboxing)

Sandkasse-testing innebærer å isolere den eldre koden fra resten av systemet, slik at du kan teste den i et kontrollert miljø. Dette kan gjøres ved å lage mocker eller stubber for avhengigheter eller ved å kjøre koden i en virtuell maskin.

Mikado-metoden

Mikado-metoden er en visuell problemløsningstilnærming for å takle komplekse refaktoreringsoppgaver. Det innebærer å lage et diagram som representerer avhengighetene mellom forskjellige deler av koden og deretter refaktorere koden på en måte som minimerer innvirkningen på andre deler av systemet. Hovedprinsippet er å "prøve" endringen og se hva som går i stykker. Hvis det går i stykker, gå tilbake til den siste fungerende tilstanden og registrer problemet. Adresser deretter det problemet før du prøver den opprinnelige endringen på nytt.

Verktøy for refaktorering

Flere verktøy kan hjelpe til med refaktorering, automatisere repetitive oppgaver og gi veiledning om beste praksis. Disse verktøyene er ofte integrert i integrerte utviklingsmiljøer (IDE-er):

Eksempel: Et utviklingsteam som jobber med en C#-applikasjon for et globalt forsikringsselskap bruker Visual Studios innebygde refaktoreringverktøy for å automatisk gi nytt navn til variabler og trekke ut metoder. De bruker også SonarQube for å identifisere kodelukter og potensielle sårbarheter.

Utfordringer og risikoer

Refaktorering av eldre kode er ikke uten utfordringer og risikoer:

Beste praksis

For å redusere utfordringene og risikoene forbundet med refaktorering av eldre kode, følg disse beste praksisene:

Konklusjon

Refaktorering av eldre kode er en utfordrende, men givende oppgave. Ved å følge strategiene og beste praksisene som er beskrevet i denne guiden, kan du temme udyret og transformere dine eldre systemer til vedlikeholdbare, pålitelige og høytytende ressurser. Husk å tilnærme deg refaktorering systematisk, teste ofte og kommunisere effektivt med teamet ditt. Med nøye planlegging og utførelse kan du låse opp det skjulte potensialet i din eldre kode og legge grunnlaget for fremtidig innovasjon.