Hopp til hovedinnhold

Teknologi / 4 minutter /

Enkle regler for vedlikeholdbar kode

Hvorfor er det viktig at en kodebase er vedlikeholdbar? Hva kjennetegner en vedlikeholdbar kodebase? I denne artikkelen skriver jeg om dette temaet, basert på erfaring fra arbeid med mange kodebaser i løpet 20 år som konsulent for kunder innenfor bank, finans og sikkerhet.

Kode: Faktorer som muliggjør videre eksistens

Hva i alle dager handler denne artikkelen om, og er den relevant for meg? - tenker du kanskje nå? Selv om tittelen inneholder ordet "kode" mener jeg artikkelen er relevant for alle som jobber med utvikling av programvare, ikke bare for utviklere.

Ordet vedlikeholdbar finner jeg ikke i den norske ordboka, men det tilsvarende uttrykket maintainable er oppført som et adjektiv for maintain i den engelske ordboka. Videre er maintain definert som:

1: cause or enable (a condition or situation) to continue
2 provide with necessities for life or existence.

Jeg liker den engelske definisjonen godt, og velger å definere vedlikeholdbar som: Faktorer som muliggjør videre eksistens. Artikkelen handler altså om vedlikeholdbar kode, og hvorfor det er så innmari viktig.

Jeg mener det finnes noen få retningslinjer man kan følge, slik at "faktorer som muliggjør videre eksistens" er tilstede i en kodebase. Jeg kommer tilbake til disse retningslinjene, men først vil jeg si hvorfor vedlikeholdbar kode er viktig.

Hvorfor er dette viktig?

Jeg mener temaet vedlikeholdbar kode er viktig. Ja, for å sette det litt på spissen, så viktig at det faktisk er snakk om faktorer som muliggjør videre eksistens (av kodebasen)! Jeg synes det ofte er lite fokus på vedlikeholdbar kode, hva kan være årsaken til det? Kanskje ligger det noe i at dette handler om detaljer på et teknisk nivå, og at de som blir mest berørt ikke har noe forhold til det? Denne artikkelen er et forsøk på å løfte temaet frem i lyset.

For deg som systemeier er kanskje økonomi den viktigste driveren for å ha vedlikeholdbar kode. Min påstand er at kostnaden med utvikling av programvare er proposjonal med hvor vedlikeholdbar kodebasen er. Og et godt grunnlag for vedlikeholdbar kode bør legges tidlig i utviklingen, altså i den første prosjektfasen. Så, dess tidligere man setter fokus på dette temaet, dess bedre er det.

For både utviklingsteamet og systemeier vil fart være en viktig driver. Man vil kunne gjøre endringer raskere dess mer vedlikeholdbar kodebasen er. Fart er viktig for at utviklingsteamet skal kunne jobbe effektivt: Med raske endringer vil man vil få tidlig tilbakemelding og kunne gjøre korreksjoner forløpende. Og samtidig er det et konkurransefortrinn for systemeier, man får ny funksjonalitet raskt ut i markedet.

Fart er også viktig med tanke på trivsel for utviklerne - det er moro å jobbe med en kodebase som lar deg utvide systemet raskt. Og fornøyde utviklere er en god ting :-)

Hva kjennetegner en vedlikeholdbar kodebase?

Etter min mening kan en kodebase, langt på vei, karakteriseres som vedlikeholdbar dersom følgende faktorer er tilstede:

  • Det er åpenbart hvor man skal endre koden. Dette handler om at koden er strukturert på en måte som er oversiktlig. Det handler også om lesbarhet, at koden inneholder begreper og navn som entydig beskriver hva den handler om.
  • En endring berører en (relativt) isolert del av koden. Det skal altså være mulig å gjøre en endring med liten risiko for å innføre regresjonsfeil. Dette handler også litt om struktur på kodebasen, og da spesielt hvordan man deler inn koden i moduler.
  • Få avhengigheter til tredjepartsbiblioteker. Dette punktet har jeg med fordi tredjepartsavhengigheter representerer kompleksitet og avhengighet til ukjent kode. Og jeg ser at tredjepartsavhengigheter brukes ukritisk i noen kodebaser.

Etter min erfaring er det noen få, enkle retningslinjer som bidrar mye til å gjøre en kodebase vedlikeholdbar. Disse retningslinjene handler om navngiving, modularisering og avhengigheter til tredjepartsbiblioteker.

#1 - God navngiving

Bruk navn som beskriver domenet. Dette gjelder forsåvidt ikke bare i kodebasen, men i alt skriftlig materiale som vedrører systemet. For eksempel i tekniske skisser og diagrammer, beskrivelser av brukerreiser, UX-prototyper og alle typer dokumentasjon. Et mye benyttet begrep på dette er "allestedsnærværende navngiving", eller "ubiquitous naming" på engelsk.

Bruk spesifikke, entydige navn, for å unngå tvil om hva man egentlig omtaler. Det vil si at man bruker samme navn på samme entitet eller konsept overalt. Og ikke bruk samme (eller lignende) navn på forskjellige entiteter/konsepter, det skaper også forvirring.

Eksempler på navngiving
Eksempler på navngiving

Gi navn ut fra hva som blir gjort i koden, ikke hvordan det blir gjort. For eksempel, ikke lag navn basert på hvilken teknologi som er brukt, men hvilken rolle teknologien spiller i systemet. Hvis koden starter Jetty for å eksponere et API ved hjelp av REST, ikke kall modulen for "jetty", men for eksempel "rest-api". Hvis man senere bytter til å bruke Tomcat i stedet for Jetty er "jetty" et misvisende navn. Gjerne også gjør navnet mer presist, for eksempel kall et API for administrasjon av systemet for "admin-rest-api".

#2 - Fornuftig modularisering

Med fornuftig modularisering mener jeg at koden er bygd opp av moduler, med høy kohesjon. Og at modulene er bygd på en måte som muliggjør løse koblinger mellom forskjellige moduler. Jeg skal forklare nærmere hva jeg mener med egenskapene høy kohesjon og løse koblinger. Det som er viktig er at kombinasjonen av disse egenskapene bidrar til å kunne gjøre en endring uten at det berører en uforholdsmessig stor del av koden.

Jeg skal kort forklare begrepene høy kohesjon og løse koblinger. Det er ikke så farlig om du hopper over denne delen, poenget er at i alle fall arkitekter bør ha et forhold til dette.

Fornuftig modularisering
Fornuftig modularisering

De funksjonelle modulene kunde-bestilling og annen-funksjonalitet har en løs kobling til modulen sms-api, og ingen direkte avhengigheter til den tekniske modulen sms-integrasjon.

De funksjonelle modulene kunde-bestilling og annen-funksjonalitet har en løs kobling til modulen sms-api, og ingen direkte avhengigheter til den tekniske modulen sms-integrasjon.

Høy kohesjon betyr at en modul har én bestemt oppgave. Denne ene oppgaven kan være på et teknisk eller funksjonelt nivå. Et eksempel på en teknisk modul kan være integrasjon mot en ekstern tjeneste for sending av SMS. En funksjonell modul kan for eksempel være bestilling av en vare fra en kunde. Tekniske moduler vil typisk benyttes av flere funksjonelle moduler, og da kanskje via en løs kobling (se forklaring nedenfor). En teknisk modul kan vi kalle en biblioteksmodul, og vil ha ett generelt design uten domenespesifikk logikk for å kunne brukes i forskjellige samenhenger. En funksjonell modul kan vi kalle en applikasjonsmodul, og vil inneholde domenespesifikk kode. En funksjonell modul vil i mindre grad benyttes fra andre (funksjonelle) moduler enn en teknisk modul, men hvis det skjer er det alltid via løse koblinger.

Løse koblinger medfører at en modul ikke kjenner (mye av) resten av applikasjonen, eller i hvilken kontekst den brukes. Det vil også bety at modulen ikke eksponerer for mye av seg selv. I praksis vil det si at man begrenser hva som gis inn til modulen ved oppstart og kjøring, og at modulen ikke lekker implementasjonsdetaljer.

Løse koblinger kan implementeres ved å definere grensesnitt mellom moduler, som modulene kommuniserer via. En endring av koden i en modul trenger da ikke nødvendigvis medføre endring i andre moduler, siden det ikke er en direkte kobling mellom modulene. Samtidig bør man ha en pragmatisk tilnærming til innføring av slike grensesnitt. Overdreven bruk av grensesnitt mellom moduler fører til mange abstraksjonsnivåer, som gjør koden vanskeligere å lese og navigere i.

For videre lesning om dette temaet, søk på "hexagonal architecture" eller "onion architecture".

#3 - Få tredjepartsbiblioteker

Ha et bevisst forhold til bruken av tredjepartsbiblioteker. Et bibliotek fra en tredjepart er kode man ikke har kontroll på. De kan være kompliserte å ta i bruk, og kan inneholde sikkerhetssårbarheter og "bugs".

Et eksempel på noe kan senke utviklingshastighet og kvalitet på koden: En nyoppdaget sårbarhet i et tredjepartbibliotek forårsaker et vedlikeholdsbehov, uten at det er en ønsket, funksjonell endring som er årsaken. Den nødvendige oppgraderingen av biblioteket medfører regresjonstesting, og i verste fall regresjonsfeil. Eller enda verre: Det finnes ingen ny versjon av biblioteket som migrerer sårbarheten. Da må man vente til en ny versjon kommer, bytte til et annet bibliotek eller skrive funksjonaliteten man trenger selv.

Oppsummering

Vedlikeholdbar kode er viktig! Ofte snakker vi om at "teknisk gjeld" bygger seg opp i koden, fordi man har det travelt og tar snarveier, eller fordi man har tatt noen dårlige designvalg. Teknisk gjeld vil senke farten på videre utvikling, og dermed øke de løpende kostnadene. Jeg mener at å ha fokus på å skrive vedlikeholdbar kode er en måte å forebygge teknisk gjeld.

Ved å ha fokus på entydig, allestedsnærværende navngiving, god modularisering og et bevisst forhold til bruk av tredjepartsavhengigheter skaper du et godt grunnlag for vedlikholdbar kode. Applikasjonsutvikling og -arkitektur er et stort fagområde, og det er selfølgelig andre aspekter som også er viktige. Jeg vil likevel argumentere for at ved å følge de tre retningslinjene i denne artikkelen vil du være på god vei mot en vedlikeholdbar kodebase, med rett til videre eksistens.