Kunden frågar: "Om det skulle ta en vecka att lägga till den här funktionen i en ny app, varför behövs det tre veckor här?"
Det är en mycket bra fråga.
Och ofta lyder svaret inte: "för att utvecklarna arbetar långsammare".
Problemet kan ligga betydligt djupare - i systemets arkitektur, dess beroenden, hur data lagras, brist på tester, historiska beslut och påföljande förändringar som lagts till under åren.
Just därför är kostnaden för mjukvaruutveckling inte konstant.
Samma funktion kan kosta en helt annan summa i två olika system.
Kod värderas inte bara utifrån antalet funktioner
Vid första anblick kan uppgiften verka banal.
"Låt oss lägga till möjligheten att exportera data till Excel."
Eller: "Låt oss lägga till en ny användarroll."
Eller: "Låt oss koppla systemet till vårt CRM."
Problemet är att en funktion aldrig existerar helt frikopplad från resten av systemet.
Ny funktionalitet kan kräva ändringar i:
- databasen,
- API:et,
- backend,
- frontend,
- behörighetssystemet,
- loggningen,
- rapporteringen,
- integrationerna,
- testerna,
- cachemekanismerna,
- dokumentationen,
- releaseprocessen.
Ju mer sammanlänkat systemet är, desto fler delar måste analyseras före en ändring.
Den största kostnaden kan uppstå innan den första kodraden skrivs
I ett moget system ska utvecklaren inte bara börja koda.
Först måste man svara på:
- Var ska den här funktionen läggas till?
- Vilka moduler kommer den att kommunicera med?
- Vilka data använder den?
- Omfattas den av befintliga behörighetsmekanismer?
- Kommer ändringen att påverka andra processer?
- Vilka tester behöver uppdateras?
- Gör den nuvarande arkitekturen ens det möjligt att göra detta korrekt?
Allt detta är en del av kostnaden för att skapa funktionen.
Därför kan en stor del av arbetet i ett gammalt system vara inte själva programmeringen, utan att identifiera beroenden och begränsningar i den befintliga lösningen.
Technical debt fungerar som ränta
Ett bra sätt att tänka på technical debt är just kostnaden för framtida ändringar.
Om en viss lösning en gång gjordes snabbt, kan det ha varit helt motiverat.
Problemet uppstår när den tillfälliga lösningen blir en permanent del av systemet.
En ny funktion tillkommer.
Sedan ännu en.
Ett undantag dyker upp.
Sedan ännu ett undantag.
Därtill kommer en integration, en genväg runt problemet, en manuell process och en extra regel.
Efter några år minns ingen längre varför systemet fungerar på just det här sättet.
Men varje ny ändring måste ta hänsyn till alla dessa historiska beslut.
Martin Fowler beskriver technical debt som den extra ansträngning som uppstår vid förändringar i ett system på grund av problem med dess interna kvalitet.
Man kan alltså säga: technical debt behöver inte stoppa utvecklingen direkt. Först gör den varje ny ändring dyrare.
Signal ett: "när vi ändå håller på måste vi rätta fem saker till"
Det är ett av de mest typiska symptomen.
Kunden beställer en funktion.
Under analysen visar det sig att för att införa den måste man:
- förbättra tabellstrukturen,
- ändra autentiseringsmetoden,
- uppdatera biblioteket,
- rätta det gamla API:et,
- skriva om en del av frontend.
Plötsligt är den lilla funktionen inte längre liten. Inte för att kravet är komplicerat. Utan för att systemet inte längre har lämpliga arkitektoniska gränser.
Signal två: en ändring kräver testning av hela systemet
Om en liten ändring kräver full manuell regression får organisationen betala för bristen på automatisering.
I takt med att systemet växer ökar antalet möjliga kombinationer.
Utan ett tillräckligt testpaket blir det allt svårare att vara säker på att den nya funktionen inte har förstört den gamla.
Det i sin tur leder till försiktighet.
Driftsättningar blir mer sällsynta.
Ändringar blir större.
Risken ökar.
Och större driftsättningar är svårare att felsöka vid problem.
En ond cirkel uppstår.
Signal tre: "den modulen är bättre att inte röra"
Den meningen borde tända en varningslampa.
Om en viss modul har blivit ett område som teamet undviker eftersom dess beteende är oförutsägbart, har systemet ett betydande underhållsproblem.
Ännu värre är det om bara en person känner till hur den fungerar. Då har företaget inte bara technical debt. Det har också knowledge risk.
Att en medarbetare lämnar kan innebära att kunskap som behövs för att vidareutveckla systemet på ett säkert sätt går förlorad.
Signal fyra: varje funktion kräver undantag
Ett väl utformat system bör ha förutsägbara regler.
Om varje ny funktion kräver att man lägger till ett specialundantag, ett extra villkor eller en individuell väg, börjar arkitekturen sannolikt begränsa utvecklingen.
Det leder ofta till kod som inte längre går att förutsäga särskilt lätt.
Och brist på förutsägbarhet innebär högre kostnader för analys, testning och underhåll.
Måste allt skrivas om?
Nej.
Och här kommer vi till en mycket viktig distinktion.
Technical debt betyder inte automatiskt att en rewrite är nödvändig.
Möjliga lösningar omfattar:
Refaktorering
Alltså att förbättra strukturen i den befintliga koden utan att ändra dess affärsmässiga beteende.
Det är en bra väg när systemet fortfarande har en rimlig arkitektur, men vissa delar är svåra att underhålla.
Modernisering av utvalda komponenter
Man behöver inte byta ut hela applikationen.
Man kan börja med den mest problematiska modulen, integrationen eller lagret.
Stegvis migrering
Nya delar kan fungera bredvid det gamla systemet, och successivt flyttas fler områden över.
Detta tillvägagångssätt gör det möjligt att minska risken med en engångsmigrering. I litteraturen om legacy modernization används ofta just stegvis frikoppling av funktionalitet och ersättning av systemets olika delar.
Rewrite
Att bygga ett nytt system är meningsfullt när den nuvarande arkitekturen är så begränsande att fortsatt modernisering inte ger en motiverad avkastning.
Men en rewrite bör vara ett beslut som grundas i analys, inte en reaktion på teamets frustration.
När är det ännu inte värt att investera i modernisering?
Technical debt i sig är inte en anledning att stoppa utvecklingen. Varje system har en viss nivå av teknisk skuld. Ibland är det inte ekonomiskt motiverat att betala av den.
Om applikationen:
- fungerar stabilt,
- är säker,
- har få ändringar,
- hanterar en process som inte kommer att utvecklas nämnvärt,
- inte skapar operativa problem,
kan det vara rationellt att låta den vara i sitt nuvarande skick.
Det handlar inte om att varje system ska vara teknologiskt perfekt.
Det handlar om att skuldnivån ska vara ett medvetet beslut.
När blir kostnaden för skuld ett affärsproblem?
När den börjar påverka företagets resultat.
Till exempel:
En ny funktion skulle lanseras på marknaden inom en månad, men behöver tre.
Integrationen med en ny partner drar ut på tiden, eftersom API:et i det gamla systemet inte enkelt kan hantera nya data.
En nyckelperson i teamet måste vara med varje gång, eftersom det bara är hen som känner till den gamla modulen.
Varje större driftsättning kräver flera timmars regressionstestning.
En konkurrent lanserar nya funktioner snabbare, eftersom deras plattform gör det lättare att experimentera.
I det här läget slutar technical debt att vara ett problem för IT-avdelningen.
Det blir ett affärsproblem.
Hur mäter man om situationen förvärras?
Man behöver inte skapa ett komplicerat KPI-system.
Det är värt att följa några enkla nyckeltal:
Lead time - hur lång tid som går från att arbetet med en ändring påbörjas tills den driftsätts.
Driftsättningsfrekvens - hur ofta teamet säkert kan leverera ändringar.
Change failure rate - hur ofta driftsättningar orsakar problem.
Återställningstid - hur snabbt man kan återgå till stabil drift efter ett fel.
Ledtid för funktioner - om liknande uppgifter kräver allt större insatser.
Dessutom är det värt att analysera antalet manuella moment, testtäckning, aktualitet i beroenden och den tid som behövs för att onboarda en ny utvecklare i projektet.
Sådana data gör det möjligt att se om problemet faktiskt är tekniskt, eller om det kanske beror på processen, kraven eller sättet arbetet är organiserat på.
Den värsta lösningen är "ännu en snabb fix"
Om teamet vet att arkitekturen behöver förändras, men varje gång skjuter upp frågan, kan systemet hamna i en spiral.
"Låt oss göra en workaround nu."
"Vi gör refaktoreringen senare."
"För nu räcker det."
"Vid nästa release."
Problemet är att nästa release kommer med nya krav.
Och varje ny workaround ökar kostnaden för nästa förändring.
Därför bör beslutet om att betala av technical debt vara en del av produktens utvecklingsstrategi, inte en slumpmässig reaktion på en kris.
En bra applikation är inte en som aldrig åldras
Varje system kommer att förändras.
Tekniker kommer att förändras.
Kunder kommer att få nya behov.
Nya integrationer kommer att tillkomma.
Sättet företaget arbetar på kommer att förändras.
Därför bör målet inte vara att skapa en applikation som aldrig behöver moderniseras. Målet bör vara att skapa en sådan arkitektur, att modernisering är möjlig utan att stoppa verksamheten. Det är en enorm skillnad.
För det bästa systemet är inte det som ser mest modernt ut den dag det tas i bruk. Det är det system som också efter några år gör det möjligt för företaget att snabbt reagera på förändringar.
Och om varje ny funktion kostar allt mer, betyder det inte alltid att funktionen är svår.
Det kan vara systemet i sig som redan har blivit svårt.



