Sponsoreret indhold

Softwareudvikler: Sådan skrotter vi 20 år gammelt legacy-system

Høreapparatvirksomheden Widex er i gang med at udskifte et tungt legacy-system ved at indføre fem principper for softwareudvikling, som ifølge en af udviklerne gør det nemmere at skrive ny kode.
Af TECH RELATIONS for Widex

Hvordan skiller man sig af med et gammelt og tungt it-system uden at brænde fingrene? Det spørgsmål er der mange udviklere, der stiller sig selv i øjeblikket i takt med, at systemer hos alt fra banker til offentlige myndigheder er blevet pensionsmodne. Problemstillingen er klassisk: Gamle afhængigheder mellem komponenter kan gøre det til en næsten umulig opgave at vedligeholde kode og tilføje ny funktionalitet, uden det vælter korthuset.

Det var også den problemstilling, som danske Widex for nylig fandt en løsning på, da høreapparatvirksomheden skulle forny et over 20 år gammelt it-system og omstille sig til agil udvikling. Systemet består af tilpasningssoftware, som bruges af hørespecialister til at tilpasse høreapparaterne til den enkelte bruger. Før i tiden brugte specialisterne – eller audiologerne – mest stationære pc’er til arbejdet, men fremover vil det også foregå på tablets og telefoner. Og det var her, softwareudviklerne for alvor indså systemets begrænsning.

Blandt andet kan systemet kun køre på Windows 32 bit, hvilket allerede var en hæmsko. Derudover er dele af systemet udviklet i det aldrende programmeringssprog Delphi, hvor det er en risiko for at miste support på compileren.

Resultatet blev enhver softwareudviklers mareridt: En enorm mængde af indbyrdes afhængigheder i koden, som gjorde selv små ændringer til omfattende arbejde.

»Jeg er den eneste, der tør gå ind og rette i visse dele af koden. Det er et kæmpe problem,« siger softwarearkitekt hos Widex Søren Henningsen og uddyber:

»Det, der kendetegner legacy-kode, er, at det er svært at rette i det, fordi man ikke ved, om man så ender med at ødelægge noget andet.«

Men hvor nogle organisationer ender med blot at udskifte et legacy-system med et andet og dermed ikke løse problemet, har udviklerne hos Widex angrebet udfordringen anderledes.

Nye udviklingsprincipper vender afhængigheder på hovedet

Det er nogle komplicerede algoritmer, der ligger bag arbejdet med at tilpasse høreapparater til enkelte personers høretab.

Hver bruger kan således have nedsat hørelse på bestemte frekvenser, som høreapparatet skal tage højde for. Og med op mod 32 forudindstillede lydprogrammer i et høreapparat giver det ifølge Søren Henningsen over 15.000 parametre at skrue på.

»Det er ret komplekst. Vi kan eksempelvis ikke bare forstærke lyden lineært, men komprimerer lyden nogle steder og forstærker frekvensområder efter, hvad der er brug for,« siger han.

Læs også: Dansk høreapparatindustri søger softwareudviklere til at disrupte sig selv

Widex har derfor 50 udviklere ansat til at vedligeholde og forbedre tilpasningssoftwaren og er i øjeblikket i gang med at udvide med yderligere 50 udviklere på tværs af virksomhedens afdelinger.

Når de nye udviklere kommer til, vil de blive mødt med nogle særlige principper for objektorienteret softwareudvikling, som virksomheden indførte for nogle år tilbage for netop at blive uafhængige af den gamle legacy-platform.

Nemmere at vedligeholde og udvikle ny kode

Formålet med de fem principper, der går under betegnelsen SOLID, er ifølge Søren Henningsen at gøre det nemmere at vedligeholde og udvikle ny kode.

»Vi vil gerne ensrette koden, så alle kan se strukturen og rette i koden. Det er en måde at have en fælles forståelse, for hvis alle laver det på deres egen måde, skal du hele tiden sætte dig ind i en anden persons tankegang. Når du kender principperne bag, kan du også bedre forstå, hvad man har tænkt med en komponent og klasse,« siger han.

Læs også: Widex: En dag om ugen har man fri fra klanen til at tænke større tanker

Strategien for at komme af med legacy-systemet er at udskifte det bid for bid, forklarer softwarearkitekten. Hver gang en udvikler har lavet en ny komponent, skal den efterleve principperne, hvilket på længere sigt vil erstatte den gamle kode med noget mere fleksibelt. På den måde undgår udviklerne også at skulle skrive hele koden forfra – en proces, der før er set ende galt i blandt andet offentlige it-projekter.

»Hvis man bruger SOLID-principperne, ender man også med at komme af med legacy,« siger han og forklarer, at afhængighederne bliver vendt på hovedet, således at små ændringer i en algoritme ikke længere kræver en større omskrivning af systemet.

»Tidligere skulle man være meget påpasselig og gennemteste ved selv de mindste ændringer. Men efter vi begyndte at udvikle efter principperne, kan vi nu lettere rette fejl og tilføje funktionalitet, uden det påvirker andre komponenter. Det giver større tillid til, at koden stadig virker,« siger han.

Se princippet som får tunge legacy-systemer til at fordufte

De fem SOLID-principper ifølge Søren Henningsen

1. Single responsibility principle
Hver klasse eller komponent har kun et ansvarsområde.

Eksempel:
Forestil dig et modul, der både kan beregne forstærkning i høreapparatet og tegne grafer. Dette modul kan ændres af to årsager: At algoritmen bag beregningen skal ændres, eller at for eksempel farven på grafen skal ændres. Disse to ting har intet med hinanden at gøre, selvom de er beslægtet. Derfor bør de adskilles i to moduler, der har hvert deres ansvarsområde. Det vil gøre hvert modul mere robust.

»Hvis man bruger SOLID-principperne, ender man også med at komme af med legacy,« siger Søren Henningsen, softwarearkitekt hos Widex, og forklarer, at afhængighederne bliver vendt på hovedet, således at små ændringer i en algoritme ikke længere kræver en større omskrivning af systemet. Illustration: Widex

2. Open/closed principle
En klasse skal være åbne for udvidelse, men lukket for ændringer. Så hvis jeg tilføjer funktionalitet, skal jeg ikke ændre i en klasse, men i stedet tilføje funktionaliteten som f.eks. et plugin. Fordelen er, at du ikke skal ændre i eksisterende kode.

Eksempel:
Hvis vi har et modul, der kan beregne forstærkning, vil vi gerne kunne udvide dette modul med f.eks. nye algoritmer. Princippet siger så, at modulet skal være lukket for ændringer, men åbent for udvidelser. Det betyder, at de nye algoritmer skal kunne tilføjes uden at ændre i modulet. Det gøres ved at lave algoritmerne som plugins til modulet.

3. Liskov substitution principle
Princippet siger, at man skal kunne tage en hovedklasse og få den til at repræsentere alle specialiseringer i hele systemet. Hvis der sker en ændring i en specialisering, skal man ikke ændre hovedkomponenten.

Eksempel:
I vores kode har vi f.eks. en baseklasse for vores algoritmer, lad os kalde den BaseAlgoritme. Lad os så sige, at vi har to algoritmer, der nedarver fra denne baseklasse: Algoritme1 og Algoritme2. Princippet siger så, at hvor som helst i koden, hvor vi bruger BaseAlgoritme, der skal man kunne indsætte Algoritme1 eller Algoritme2. Hvis man ikke kan det, overholder man ikke princippet.

4. Interface segregation principle
Man adskiller komponenter via interfaces. Det betyder, at man kan ændre i en komponent, uden man behøver ændre andre komponenter, fordi interfacet er det samme.

Eksempel:
Lad os antage, at man har et stort modul, der ligesom i eksemplet fra single responsibility-princippet både kan beregne forstærkning og tegne grafer. Der er flere forskellige brugere af sådan et modul: et høreapparatsobjekt og en brugergrænseflade. Umiddelbart kender brugergrænsefladen til alle de metoder til at beregne forstærkning, som høreapparatsobjektet kender, og omvendt kender høreapparatsobjektet til alle de metoder til at tegne kurver, som brugergrænsefladen kender til. Det er ikke ønskværdigt, for det betyder, at en ændring i måden at beregne forstærkning på vil påvirke brugergrænsefladen.
Løsningen er at adskille de to funktionsområder i hver deres interface, som så kun er kendt af enten høreapparatsobjektet eller brugergrænsefladen.

5. Dependency inversion principle
Princippet går ud på at afkoble softwaremodulernes indbyrdes afhængighed, således at det normale forhold mellem moduler på et højt og lavt niveau vendes om.

Eksempel:
I vores kode har vi forskellige moduler til beregning af forstærkning. Dette er meget low level moduler. Andre moduler, der bruger disse moduler, er high-level moduler. Dependency inversion-princippet skal sikre, at ændringer i low-level moduler ikke påvirker high-level moduler, det vil sige, hvis vi f.eks. har ændringer til algoritmerne bag forstærkningen. Dette gøres ved at lade high-level modulerne definere de interfaces mod low-level modulerne, som low-level modulerne så implementerer. Så bliver high-level modulerne kun påvirket, hvis det er selve interfacet, der ændrer sig, men er upåvirket af ændringerne i low-level modulerne.