Kunden spørger: "Hvis det at tilføje denne funktion i en ny app ville tage en uge, hvorfor kræver det så tre uger her?"
Det er et rigtig godt spørgsmål.
Og ofte lyder svaret ikke: "fordi udviklerne arbejder langsommere".
Problemet kan ligge meget dybere - i systemets arkitektur, dets afhængigheder, måden data lagres på, mangel på tests, historiske beslutninger og de efterfølgende ændringer, der er blevet lagt til gennem årene.
Det er netop derfor, omkostningen ved softwareudvikling ikke er konstant.
Den samme funktion kan koste et helt andet beløb i to forskellige systemer.
Kode prissættes ikke kun efter antallet af funktioner
Ved første øjekast kan opgaven se banal ud.
"Lad os tilføje muligheden for at eksportere data til Excel."
Eller: "Lad os tilføje en ny brugerrolle."
Eller: "Lad os forbinde systemet med vores CRM."
Problemet er, at en funktion aldrig eksisterer helt løsrevet fra resten af systemet.
Ny funktionalitet kan kræve ændringer i:
- databasen,
- API'et,
- backend'en,
- frontend'en,
- rettighedssystemet,
- logning,
- rapportering,
- integrationer,
- tests,
- cache-mekanismer,
- dokumentation,
- implementeringsprocessen.
Jo mere forbundet systemet er, desto flere elementer skal gennemgås før en ændring.
Den største omkostning kan opstå, før den første linje kode skrives
I et modent system bør udvikleren ikke bare begynde at kode.
Først skal man svare på:
- Hvor skal denne funktion tilføjes?
- Hvilke moduler skal den kommunikere med?
- Hvilke data bruger den?
- Dækker de eksisterende rettighedsmekanismer den?
- Vil ændringen påvirke andre processer?
- Hvilke tests skal opdateres?
- Tillader den nuværende arkitektur overhovedet, at dette kan gøres korrekt?
Alt dette er en del af omkostningen ved at skabe en funktion.
Derfor kan en stor del af arbejdet i et gammelt system bestå ikke i selve kodningen, men i at kortlægge afhængigheder og begrænsninger i den eksisterende løsning.
Technical debt fungerer som renter
En god måde at tænke på technical debt er netop prisen på de næste ændringer.
Hvis en bestemt løsning engang blev lavet hurtigt, kan det være helt berettiget.
Problemet opstår, når en midlertidig løsning bliver en permanent del af systemet.
Der kommer endnu en funktion.
Så endnu en.
Der opstår en undtagelse.
Så endnu en undtagelse.
Dertil kommer integration, en omgåelse af problemet, en manuel proces og en ekstra regel.
Efter nogle år er der ingen, der længere husker, hvorfor systemet fungerer på netop den måde.
Men hver ny ændring må tage højde for alle disse historiske beslutninger.
Martin Fowler beskriver technical debt som den ekstra indsats, der skal bruges ved ændringer i et system som følge af problemer med dets interne kvalitet.
Man kan derfor sige: technical debt behøver ikke at stoppe udviklingen med det samme. Først gør det hver ny ændring dyrere.
Tegn ét: "i samme omgang skal vi også rette fem andre ting"
Det er et af de mest karakteristiske symptomer.
Kunden bestiller én funktion.
Under analysen viser det sig, at for at implementere den, skal man:
- rette tabelstrukturen,
- ændre autoriseringen,
- opdatere biblioteket,
- rette det gamle API,
- omsrive en del af frontend'en.
Pludselig er en lille funktion ikke længere lille. Ikke fordi kravet er kompliceret. Men fordi systemet ikke længere har de rette arkitektoniske grænser.
Tegn to: en ændring kræver test af hele systemet
Hvis en lille ændring kræver en fuld manuel regressionstest, betaler organisationen for manglende automatisering.
I takt med at systemet vokser, vokser antallet af mulige kombinationer.
Uden et passende sæt tests bliver det stadig sværere at være sikker på, at den nye funktion ikke har ødelagt den gamle.
Det fører igen til forsigtighed.
Udrulninger bliver sjældnere.
Ændringer bliver større.
Risikoen vokser.
Og større udrulninger er sværere at diagnosticere, hvis der opstår problemer.
Der opstår en ond cirkel.
Tegn tre: "det modul bør vi helst ikke røre ved"
Den sætning bør få alarmklokkerne til at ringe.
Hvis et bestemt modul er blevet et område, som teamet undgår, fordi dets opførsel er uforudsigelig, har systemet et væsentligt vedligeholdelsesproblem.
Endnu værre er det, hvis kun én person kender dets funktion. Så har virksomheden ikke kun technical debt. Den har også knowledge risk.
At en enkelt medarbejder stopper, kan betyde tab af den viden, der er nødvendig for sikkert at videreudvikle systemet.
Tegn fire: hver funktion kræver undtagelser
Et godt designet system bør have forudsigelige regler.
Hvis hver ny funktion kræver, at der skrives en særlig undtagelse, en ekstra betingelse eller en individuel vej, begynder arkitekturen sandsynligvis at begrænse udviklingen.
Det fører ofte til kode, som ikke længere er let at forudsige.
Og mangel på forudsigelighed betyder højere omkostninger til analyse, test og vedligeholdelse.
Skal alt omskrives?
Nej.
Og her kommer vi til en meget vigtig skelnen.
Technical debt betyder ikke automatisk, at man skal lave en rewrite.
Mulige løsninger omfatter:
Refaktorisering
Altså at forbedre strukturen i den eksisterende kode uden at ændre dens forretningsmæssige adfærd.
Det er en god retning, når systemet stadig har en fornuftig arkitektur, men de konkrete dele er svære at vedligeholde.
Modernisering af udvalgte komponenter
Man behøver ikke udskifte hele applikationen.
Man kan begynde med det mest problematiske modul, integration eller lag.
Gradvis migrering
Nye elementer kan køre ved siden af det gamle system, og de næste områder flyttes løbende.
Denne tilgang gør det muligt at begrænse risikoen ved en engangsmigrering. I litteraturen om legacy modernisation bruger man ofte netop gradvis udskillelse af funktionalitet og udskiftning af de enkelte dele af systemet.
Rewrite
At bygge et nyt system giver mening, når den nuværende arkitektur er så begrænsende, at yderligere modernisering ikke giver et rimeligt afkast.
Men en rewrite bør være en beslutning, der udspringer af analyse, ikke en reaktion på teamets frustration.
Hvornår kan det endnu ikke betale sig at investere i modernisering?
Teknisk gæld i sig selv er ikke en grund til at sætte udviklingen på pause. Ethvert system har et vist niveau af teknisk gæld. Nogle gange giver det ikke økonomisk mening at betale den af.
Hvis applikationen:
- kører stabilt,
- er sikker,
- har et lille antal ændringer,
- understøtter en proces, som ikke vil udvikle sig væsentligt,
- ikke skaber driftsmæssige problemer,
kan det være rationelt at lade den forblive i sin nuværende tilstand.
Det handler ikke om, at hvert system skal være teknologisk perfekt.
Det handler om, at niveauet af gæld er en bevidst beslutning.
Hvornår bliver omkostningen ved gæld et forretningsproblem?
Når den begynder at påvirke virksomhedens resultater.
For eksempel:
En ny funktion skulle på markedet om en måned, men den har brug for tre.
Integrationen med en ny partner trækker ud, fordi API'et i det gamle system ikke nemt kan håndtere nye data.
En nøgleperson i teamet må deltage i arbejdet hver gang, fordi kun vedkommende kender det gamle modul.
Hver større udrulning kræver flere timers regressionstest.
En konkurrent lancerer nye funktioner hurtigere, fordi hans platform gør det muligt at eksperimentere hurtigere.
På dette tidspunkt holder technical debt op med at være et problem for IT-afdelingen.
Det bliver et forretningsproblem.
Hvordan måler man, om situationen forværres?
Man behøver ikke at opbygge et kompliceret KPI-system.
Det er værd at følge nogle få enkle indikatorer:
Lead time - hvor lang tid der går fra arbejdet med en ændring påbegyndes, til den sættes i drift.
Udrulningsfrekvens - hvor ofte teamet sikkert kan levere ændringer.
Change failure rate - hvor ofte udrulninger skaber problemer.
Tid til genoprettelse - hvor hurtigt man kan vende tilbage til stabil drift efter et nedbrud.
Gennemførelsestid for funktioner - om lignende opgaver kræver stadig større indsats.
Dertil er det værd at analysere antallet af manuelle handlinger, testdækning, opdatering af afhængigheder og den tid, der kræves for at få en ny programmør ind i projektet.
Sådanne data gør det muligt at se, om problemet faktisk er teknisk, eller om det måske skyldes proces, krav eller måden arbejdet er organiseret på.
Den værste løsning er "bare én hurtig rettelse til"
Hvis teamet ved, at arkitekturen kræver ændringer, men hver gang udskyder emnet, kan systemet komme ind i en spiral.
"Lad os lave en workaround nu."
"Vi laver refaktoreringen senere."
"For nu er det nok."
"Ved næste release."
Problemet er, at næste release bringer nye krav.
Og hver ny workaround øger omkostningen ved den næste ændring.
Derfor bør beslutningen om at betale technical debt af være en del af produktudviklingsstrategien og ikke en tilfældig reaktion på en krise.
En god applikation er ikke en, der aldrig bliver gammel
Ethvert system vil ændre sig.
Teknologier vil ændre sig.
Kunder vil få nye behov.
Der vil komme nye integrationer.
Virksomhedens måde at arbejde på vil ændre sig.
Derfor bør målet ikke være at skabe en applikation, som aldrig skal moderniseres. Målet bør være at skabe en sådan arkitektur, hvor modernisering er mulig uden at stoppe forretningen. Det er en enorm forskel.
For det bedste system er ikke det, der ser mest moderne ud på dagen for lanceringen. Det er det system, som også efter flere år gør det muligt for virksomheden hurtigt at reagere på ændringer.
Og hvis hver ny funktion bliver dyrere og dyrere, betyder det ikke altid, at funktionen er svær.
Måske er selve systemet blevet svært.
