Småt er godt - så hvorfor skriver vi klasser på 20.000 linjer?

Illustration: Jesper Stein Sandal
Nogle gange er det bedst kun lige at skrive den kode, der er nødvendig, og ikke gå hele vejen med den komplette klasse, der kan bruges til alt.

Hvad er den ideelle størrelse på et program, målt i kodelinjer? Svaret er kompliceret, men der er fornuft i at søge mod småt snarere end stort, for selvom organisationer er vilde med at søsætte store it-projekter, så er de små ofte bedre.

Hovedproblemet med kodemængder over en vis størrelse er, at det bliver umuligt for den enkelte udvikler at holde styr på, hvad koden indeholder.

»For al software må der findes en 'passende størrelse'. Det er så et optimeringsproblem at prøve at finde den størrelse. Men udgangspunktet må være noget, der har en størrelse, så du kan rumme den i dit hoved og arbejde med den,« sagde softwarekonsulent Kevlin Henney i et oplæg på udviklerkonferencen Goto 2016 i København.

Tidlige it-systemer havde fysiske begrænsninger som eksempelvis mængden af hukommelse. De spiller en mindre rolle i dag - i hvert fald når det gælder størrelsen på den kode, udviklerne arbejder med.

Selv noget så omfattende som styresystemet Unix var i 1970'erne på nogle få tusinde linjer. Sjette udgave af Unix var på cirka 10.000 linjer, og det gjorde koden til nærmest en lærebog i sig i, hvordan et styresystem fungerer.

»I dag har jeg set Java-klasser på 20.000 linjer,« sagde Kevlin Henney.

Hvis koden passerer det punkt, hvor udvikleren har et instinktivt overblik over, hvad den indeholder, og hvad der gør hvad, så har man introduceret risikoen for at ende med et system, der er stort og svært at vedligeholde.

Homøopatisk navngivning

Kodevokseværk er ikke kun et problem med systemarkitekturen. Det begynder ofte i det små, hvor eksempelvis lange kodelinjer bliver en kilde til fejl, fordi de passerer en grænse for, hvad der er nemt at læse for et menneske.

»Selvom vores skærmteknologier har udviklet sig, så er vores visuelle kapacitet ikke fulgt med. Så 80 kolonner til tekst kan føles som en meget 70'er-agtig begrænsning, men faktisk har mennesker svært ved mere end 50-60 kolonner,« sagde Kevlin Henney.

I et konkret projekt fandt de frem til, at de ved at se på de linjer, der strakte sig langt ud over de øvrige, fandt en del fejl. En enkelt linje strakte sig mere end 300 kolonner.

Læsevenlig kode er et omdiskuteret emne i programmering, fordi ikke alle er enige om, hvad det egentligt dækker over. Meget kompakt kode med den syntaks, der bruger færrest tegn, er ikke altid lige let at læse for enhver anden udvikler, der skal gennemskue, hvad koden faktisk gør.

Omvendt kan overflødige deklarationer også gøre det sværere at holde styr på, hvad der foregår.

Et eksempel kan være navngivning af metoder og variable, hvor der kan gå inflation i det, når navnene skal afspejle programmets struktur.

»Det bliver til homøopatisk navngivning,« sagde Kevlin Henney med henvisning til, at længden til sidst fortynder betydningen så meget, at den forsvinder.

Læs også: Udvikler: Drop de alt for lange navne!

Et langt større problem er imidlertid kode, der sniger sig med ind i projekter, selvom den var tiltænkt bare at blive brugt midlertidigt. Og vi er mere tilbøjelige til at holde fast i kode, vi har skrevet, jo større den er.

Det er der ingen grund til, for i modsætning til at bygge et højhus, så er omkostningerne ved at gå lidt tilbage og prøve en ny tilgang, forholdsvis små, når man skriver software. Og med versionsstyring har udvikleren nærmest en tidsmaskine ved hånden.

Software er billigst i små pakker

Prisen på software er selv i bedste fald ikke lineær proportional med antallet af kodelinjer. Tværtimod begynder prisen at stige pr. linje, når man når over en hvis størrelse, som kræver flere test og flere hensyn, der skal tages.

Det er ikke åbenlyst for dem, der køber et system, at de får mere for pengene ved at købe små systemer. Lad os eksempelvis sige, at en offentlig myndighed skulle bruge et it-system, så vil de ofte bestille ét system, der kan understøtte et komplet område - for eksempel gældsinddrivelse.

Men der er ikke noget, der hedder mængderabat i softwareudvikling.

»Mælk er billigst, hvis du køber en stor karton i stedet for flere små kartoner. Men software er billigst i små kartoner,« sagde Kevlin Henney.

Problemet er ofte, at kunden bestiller software, der afspejler den måde, organisationen opfatter sig selv på. Men når det skal implementeres, så kommer systemet til at afspejle den måde, organisationen i virkeligheden fungerer på, og det er ofte mere kompliceret end blot eksempelvis et hierarki med adskilte afdelinger.

Derfor bør man være klar over, at virkeligheden formentligt er mere rodet, end man tror, og at det vil afspejles i det store it-system.

En af fordelene ved at skrive flere små it-systemer til afgrænsede opgaver i stedet for det store forkromede system er, at det kan klares inden for en overskuelig tidshorisont af et lille hold udviklere.

Det er nemlig et klassisk problem inden for softwareudvikling, at flere udviklere på en opgave ikke giver en tilsvarende fremdrift i projektet, hvis man har passeret et vist mætningspunkt.

Populært sagt kan man sætte så mange folk på at løse et lille problem, at det ender med at blive et stort problem, fordi flere mennesker indebærer mere interaktion og kommunikation, så der bruges forholdsvis flere ressourcer på overhead i projektorganisationen.

»Når du over en vis størrelse, så tilføjer du folk for at kompensere for, hvor mange folk, du har på opgaven,« sagde Kevlin Henney.

Han advokerer for, at man i sine projekter tænker i 'bæredygtig softwareudvikling'. Det vil sige kode, der opfylder nutidens behov, men uden at det begrænser fremtidige generationers muligheder for tilsvarende at opfylde deres behov - en definition lånt fra bæredygtighed i miljøsammenhæng.

Et af rådene er dog lidt i modstrid til, hvad man taler om i forhold til miljø og ressourceforbrug:

»Vi skal blive bedre til at tale om, hvordan vi smider kode væk,« sagde Kevlin Henney.

Tips og korrekturforslag til denne historie sendes til tip@version2.dk
Følg forløbet
Kommentarer (23)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
Morten Toudahl

Er da et klart defineret antipattern. Og også en umulighed hvis man følger single responsibility princippet i SOLID. Eller high cohesion fra GRASP.

Så hvordan et professionelt software firma kan ende med en klasse på 20k linjer, har jeg svært ved at forstå...

Martin Jünckow

Der er flere årsager til at de færreste projekter er godt organiserede.

For det første er virksomhederne somregel for resultat-orienterede, hvilket betyder at langtidseffekterne af læsbar og vedligeholdelsesvenlig kode dumper ned af prioritetlisten og oftest er det første der ryger ud når der kommer lidt stress på.

For det andet har vi som udviklere også et ansvar, der er simpelthen for få der interesserer sig rigtig for det. Det betyder at for de fleste er det at skrive læsbar og vedligeholdeldesvenlig kode et spørgsmål om lige at bruge 10 minutter på at refaktorere det klamp-kode man nu har skrevet inden det skal committes - det er de færreste der formår at tænke det ind som en naturlig del af det at skrive koden i første omgang.

Reelt er min erfaring at det faktisk ikke tager længere tid at gøre det ordentligt fra start, tværtimod - bedre struktur fører til væsentlig færre bugs og lettere debugging når bugs optræder og debugging er oftest en væsentlig større tidssluger end at organisere kode. Men misforstå mig ikke, jeg påstår ikke at man kan undgå refaktorering, tværtimod, refaktorering bør ske løbende som man skriver koden - hver gang man erkender at den kode man har skrevet kan organiseres bedre.

Man kan komme langt ved at forstå GRASP og SOLID, men jeg kender mange udviklere der påstår de udvikler efter disse principper uden at de formår at skrive velstrukturet kode alligevel.

Skal vi starte lidt fra bunden er historien om Hora vs Tempus ret beskrivende, kender man den ikke kan den læses her: http://en.citizendium.org/wiki/Parable_of_the_two_watchmakers

Nøglen til at administrere kompleksitet er at konstruere løsninger mere som Hora end som Tempus, dvs. kompleksiteten holdes nede ved at bygge små overskuelige og stabile subsystemer man sætter sammen til større subsystemer - indtil man til sidst har bygget en samlet løsning.

Skal jeg give et par guidelines til hvor mange linjer kode der er for meget, så kommer her de størrelser jeg normalt arbejder med:

Metoder:
Metoder bør ikke være meget større end 10 linjer reel kode (fratræk brackets på egne linjer og den slags strukturlinjer) og bør kun have ét formål. Det formål kan godt være at organisere de 5 andre handlinger som metoden har til formål at udføre, men så bør hver af disse handlinger have sin egen metode, med sit eget beskrivende navn der dokumenterer handlingen. Vær ikke bange for at splitte metoder ud i sub-metoder indtil hver metode kun dækker ét ansvarsområde. Kommentarer der ligesom forsøger at inddele en metode i sektioner er et anti-pattern, lav istedet ordentlig navngivne metoder til de forskellige dele.

Klasser:
Generelt oplever jeg at velstruktuerede klasser sjældent er over 2-300 linjers kode. Når klassen over dette punkt bør man spørge sig selv om klassen reelt kun dækker ét ansvarsområde. Identificerer man flere ansvarsområder klassen dækker over, så vær ikke bange for at lave nogle supporterende klasser indtil hver klasse kun dækker ét ansvarsområde.

Der er selvfølgelig undtagelser, men det bør være bevidste undtagelser.

Hvilke code-smells/guidelines bruger I til at identificere dårlig struktureret kode?

Martin Kirk

Med Visual Studio (og 100 andre editors) kan man kollapse kode samt indsætte 'regions' som også kan kollapses.

At have 30 filer på 100 linjer giver på ingen måde overblik, fordi man skal åbne 10 ad gangen og switche imellem dem konstant for at ændre funktionalitet.

Rapid development giver heller ikke tid til at tænke over hvordan man deler kode op i pæne bunker.. Især ikke hvis man er presset på deadline.

Må dog indrømme at 3000 linjer er der hvor jeg har min grænse for størrelse på filer.. Og der hvor jeg begynder at smide store stykker kode over i passende util-classes og lign.

SOLID er måske godt, til nogle typer af projekter..

Martin Jünckow

Lodret uenig. Code-collapsing burde aldrig være indført i moderne editorer og bruges nu til at retfærdiggøre at skrive dårlig organiseret kode, desuden er den slags sjældent understøttet af eks. code-review tools.

I store projekter ender du anyway med tusindvis af klasser - det er en stakket frist at forsøge at løse det ved at puste de enkelte klasser mere op og bryde single-responsibility princippet. Før eller siden er du nød til at tage fat i det mere grundlæggende problem og finde en løsning der gør at du kan administrere og overskue tusindvis af klasser.

Rapid development betyder iøvrigt ikke at man bare skal klampe løs til man har en løsning og glemme al om vedligeholdelsesvenlighed... og jeg vil argumentere for at du ikke bliver mere produktiv på den måde, du ender bare med at bruge tiden på andre ting.

Morten Toudahl

Hvilke code-smells/guidelines bruger I til at identificere dårlig struktureret kode?


Som antydet i min post - så holder jeg mig til SOLID og GRASP, samt nogle få design patterns jeg kan uden ad. Når jeg mener/tror at jeg har behov for et pattern men ikke lige kan se det passende, så kigger jeg GoF's igennem for at se om jeg kan finde et der passer til situationen.

Jeg er i øvrigt begyndt at fokusere mere på SOLID her det seneste stykke tid. Jeg syntes nemlig alligevel de principper leder til GRASP principperne. Jeg mener i hvert fald at de dækker de samme områder, og giver det samme resultat.

Ud over det, så læser jeg ellers bøger og ser fordrag af Robert C. Martin - da jeg mener han har fat i den lange ende når det kommer til design af kode.
Jeg kan f.eks anbefale bøgerne "Clean code" og "Agile Principles, Patterns, and Practices".

At have 30 filer på 100 linjer giver på ingen måde overblik, fordi man skal åbne 10 ad gangen og switche imellem dem konstant for at ændre funktionalitet.


Man burde skrive en klasse færdig - og derefter så burde man ikke have behov for at redigere den igen.
Open/closed principle

Theis Blickfeldt

Hvis man har lavet en ordentligt opdelt kode, så burde det hverken være nødvendigt med code-collapse eller klasser på 3000 linjer.
Jeg vil komme med den påstand, at hvis man har skrevet 3000 linjer kode i én fil, så er der store dele af koden man kan splitte op i flere under-lag. Og hvis man kan definere hvorfor kode høre til i forskellige lag, så skal man bare komme igang med, at splitte sin kode op, for det vil altid gøre koden hhv. nemmere, at læse og nemmere at vedligeholde.
Hvis man reelt set mener, at man har 3000 linjers kode som høre sammen i samme fil/klasse, så har man ikke været god nok til, at afgrænse ansvaret for den pågældende fil/klasse.

Martin Jünckow

Jeg er i øvrigt begyndt at fokusere mere på SOLID her det seneste stykke tid. Jeg syntes nemlig alligevel de principper leder til GRASP principperne. Jeg mener i hvert fald at de dækker de samme områder, og giver det samme resultat.

Helt enig, det er samme verdenssyn blot anskuet fra forskellige vinkler. I virkeligheden synes jeg de komplimenterer hinanden godt og er med at give bedre forståelse for hinanden.

Som eksempel leder Low coupling og High Cohesion til Single Responsibility.

Morten Toudahl

Helt enig, det er samme verdenssyn blot anskuet fra forskellige vinkler. I virkeligheden synes jeg de komplimenterer hinanden godt og er med at give bedre forståelse for hinanden.

Ja, det gør de bestemt. Personligt ser jeg dog ingen grund til bruge begge, når de nu leder til det samme.
Og efter at have læst Robert C. Martin's forklaring af SOLID og Craig Larman's forklaring af GRASP fortrækker jeg klart SOLID.

Martin Jünckow

Og efter at have læst Robert C. Martin's forklaring af SOLID og Craig Larman's forklaring af GRASP fortrækker jeg klart SOLID.

Reelt synes jeg det er forkert at forsøge at stille dem op overfor hinanden som du gør der og "følge" den ene frem for den anden.

Det handler om at forstå de underliggende tanker og erfaringer der har ledt til udformningen af GRASP og SOLID og kunne omsætte det til software design. For nogle giver GRASP måske en bedre forståelse for det end SOLID og omvendt, men det er ingen religion og det er vigtigere at forstå hvorfor de her principper giver mening end at følge dem slavisk så at sige.

Kristian Mandrup

En klasse bør sjældent være mere end hvad der kan vises på et skærmbillede med standard font. Brug af code collapse er en farlig ting og giver netop anledning til at presse alt for meget funktionalitet ind der ikke hører hjemme. Beginner mistake!
En funktion bør sjældent være mere end 2-3 linjer, oftest one liners og sjældent tage argumenter, i så fald højst 1 argument... Coding is an art!

Tom Budde Dalsgaard

Det er her Paretos 20/80 regel træder i kraft, find ud hvad der er vigtigst. Lav først de 20% der gør at 80% af programmet virker. Planlæg med, at det første skal skrives om og gør det inden at det er for sent. De fleste projekter ender alligevel med knopskydninger og ændrede krav, så det kan lige så godt tænkes med ind fra starten.
Her er User Story Mapping en god hjælp.

Tobias Tobiasen

...
En funktion bør sjældent være mere end 2-3 linjer, oftest one liners og sjældent tage argumenter, i så fald højst 1 argument... Coding is an art


I mine øjne er det for meget i den anden grøft. Det er nærmest umuligt at finde ud af hvad et program gør hvis der kun er metoder med 2-3 linier. Det er i hvert fald svært for mig at læse. Der er for lidt context og "flowet" i koden er svært at gennemskue. Man ender helt sikkert op med en masse små metoder der kun bliver brugt et sted og det har jeg svært ved at se fornuften i.

Og en metode med 1 linie uden argumenter? Det har jeg svært ved at se fornuften i.

Martin Jünckow

Tobias: Mange små metoder giver bedre overblik over den handling der udføres end store metoder og virker selv-dokumenterende, fordi du kan navngive hver logisk handling - men det virker kun hvis du samtidig også holder dine klasser små og fokuserede. Jeg vil også argumentere for altid at placere private helper methods direkte under den public metode de hører til og helst i den korrekte rækkefølge som de kaldes i den public metode.

Store klasser med mange små metoder kan være et mareridt, man er nød til at gå hele vejen hvis man vil høste fordelene.

Når det er sagt så er jeg dog enig i kritikken langt hen ad vejen, jeg finder også Kristians forslag lidt for akademisk og jeg er ikke stor fan af one-liners som ofte giver dårligere læsbarhed og vedligeholdelsesvenlighed fordi udvikleren absolut skal forsøge at klemme alt ned på én linie på kreativ vis. Bare spørg Perl-udviklere.

Den med argumenterne må du meget gerne uddybe Kristian.

Torsten Nielsen

Det er nærmest umuligt at finde ud af hvad et program gør hvis der kun er metoder med 2-3 linier. Det er i hvert fald svært for mig at læse. Der er for lidt context og "flowet" i koden er svært at gennemskue.


Det afhænger meget af om der anvendes en god og meningsfuld navngivning af metoderne. Gjort godt kan det være endog meget læsbart og nærmest føles som et domænespecifikt sprog.

René Nielsen

Er i en klub?
Lidt sjovt I alle hedder Martin.
Men i såfald; TAK Martin!

Både artikel men bestemt ogå kommentarerne er virkelig god læse; men hvor finder jeg mere af den slags, uden det bliver for tørt og religiøst.....
- Ja og uden at hedde Martin ;-)

Martin Jünckow

Beklager at mit navn er Martin ;-)

Faktisk et meget godt spørgsmål, der findes en del bøger om emnet, men må indrømme jeg er lidt ude af trit med hvad der er moderne at læse idag - jeg mindes jeg fik en del ud af en bog som Code Complete i sin tid, men den er efterhånden af lidt aldrende dato, mange af tingene deri er dog stadig ganske relevante.

Ovenstående indlæg er baseret på mine egne erfaringer, efter skræmmende nok at have skrevet softwareløsninger i 2 årtier efterhånden for både små, mellem og store virksomheder.

Christoffer Buchholz

Det var satans. Jeg troede ikke der var nogen der var for så store filer, men derimod at det bare "skete" i kampens hede, og så var der ikke tid til at rydde.

Jeg prøver at holde mig til maks 500 linjer, men gerne mindre, og det er mine filer ofte også, uden at jeg skal tænke videre over det (jeg har lige tjekket et par af de projekter jeg arbejder på i øjeblikket). Hvis en fil er større, betyder det efter min erfaring, at der er funktioner og/eller klasser der gør for meget, og kan deles op, og når man så har delt disse op, kan det ofte deles ud i flere filer, som det tit giver mening.

Desuden, i forhold til det der med at skulle skifte mellem mange filer... Nu har jeg aldrig brugt Visual Studio i mere end et kvarter, men i Vim, Emacs og Xcode kan jeg have flere filer synlige på en gang ved at splitte editoren op. Nogle gange har jeg 6 filer åbne - 3 på vandret i to række, men for det meste har jeg 1-4 åbne.

Martin Kirk

Når jeg siger jeg elsker 3000 linjers filer, så er det lidt en sandhed med modifikationer.
Lidt mere præcist vil jeg sige, at jeg 'ikke har noget imod' 3000 linjers filer. Så længe der ikke er for mange af dem !

Men nogle gange giver det bare mere mening at have tonsvis af funktionalitet samlet ét sted og hvor det kan være svært at finde funktionaliteter som skal deles ud i en ny fil, uden det gøres bare for at skrumpe filen.

fx har jeg et projekt; en kode editor, med parsers, eventhandlers og definitioner hvor mange af (Javascript) filerne er på 1000+ linjer. Det er faktisk meget normalt at man med Javascript har kæmpefiler (medmindre man bruger et bundling framework).

Som Tobias skriver, så er det nemmere at kode når man kan overskue konteksten og hvis man bliver alt for akademisk og splitter alting op efter SOLID principperne og måske går for meget amok og har 1000vis af små filer i projektet, så mister man overblikket på en anden måde.

Tror den gyldne middelvej må være at filer generelt ikke må være længere end 200 linjer og at én funktionalitet i et program ikke må splittes ud over mere end 3 filer...

Christoffer Buchholz

Jeg er ikke sikker på jeg forstår det med at man ikke må splitte funktionalitet ud på mere end 3 filer, for i mit hoved giver det ingen mening.

Mener du, at man i din editor hvor du har funktionalitet der hedder søg, og denne så ikke må være beskrevet ud på mere end 3 filer? For det synes jeg er helt ude i hampen.

Søg kan deles ud i mange funktionalitet selv. F.eks. kunne det at læse indholdet af en fil til søgning være i én fil. Det giver mening. Men hvad nu hvis man pludselig skal understøtte 4 platforme der alle gør det på hver deres måde, som f.eks. dækker 100 linjer for hver platform? Ja, så synes jeg da hver deres måde skal have hver deres fil. Så skal man også kunne vise de resultater der er i filen på en grafisk måde. Det er endnu en fil, hvis man bruger et bibliotek der nemt kan gøre det for dig. hvis man derimod selv skal implementere det at markere noget tekst flot visuelt på til søgeresultatet, så er det måske 10+ filer, og så videre.

Jeg tror ikke man kan lave regler på den måde, desværre.

Anders Jensen

Lad os da få nogle sjove eksempler på gude-klasser på banen.

Jeg lægger ud med Activity klassen fra Android API'et. Der er over 100 offentlige metoder og over 7000 linjer i filen (det meste er JavaDoc). Den er et arketypisk eksempel på en gude-klasse, som man endda skal nedarve fra når man benytter den.

Klassen kan ses her: https://android.googlesource.com/platform/frameworks/base/+/master/core/...

Martin Kirk

Mener du, at man i din editor hvor du har funktionalitet der hedder søg, og denne så ikke må være beskrevet ud på mere end 3 filer? For det synes jeg er helt ude i hampen.

Det var ment mest som en slags tommelfingerregel - for at undgå at man skal åbne 10 filer ad gangen for at kode videre på en funktion i programmet / applikationen / websitet..

og når jeg siger funktionalitet så er det de små håndgribelige størrelser og ikke en hel usecase..

fx en FilesystemWatcher som holder øje med nogle filer i en mappe, den funktion kan sagtens foregå inde i een enkelt fil
alt efter hvad der skal gøres med filerne når der sker ændringer, så kan denne så sende filerne videre til andre funktioner i programmet.

Det kan også være en Map-Control som på et website håndtere GoogleMaps / Skobbler integrationen ... her kan det dog blive ret svært at holde sig under de 200 linjer kode - men man kan stadig holde sig til en håndfuld af filer som indeholder al den funktionalitet der skal til, UDEN at ende med de akademiske 50 filer på 30 linjer

Christoffer Buchholz

Det der undre mig er, at du siger "for at undgå at man skal åbne 10 filer ad gangen for at kode videre på en funktion i programmet".

Jeg har tit den samme fil åbne i to buffers på én gang, sådan at jeg kan referere til det kode jeg skriver imod.

Derudover vil jeg påpege, at jeg ikke kan mindes at jeg nogensinde har tænkt "det er godt nok irriterende at jeg skal åbne 10 filer for at implementere det her", men det er måske bare mig... Måske har jeg bare aldrig arbejdet på projekter hvor der ikke var nogen idé i at det var delt ud på 10 filer, og derfor var irriterende i stedet for at bidrage til et bedre overblik? Hvem ved :)

Log ind eller Opret konto for at kommentere