Kode skal være lett å lese. Det er vi alle enige om. Men hva betyr dette i praksis? Hva er lett å lese og hva er det koden skal formidle til leseren? Hvorfor leser vi kode i det hele tatt?
(Dette har jeg skrevet om for mange år siden forresten, men bare på engelsk.)
«Uncle Bob» (Robert C Martin) har kanskje vært den mest ivrige forkjemperen for dette med «clean code» og lesbarhet. Målet han sier vi skal oppnå er å abstrahere alt til hver del, hver funksjon, hver klasse/modul kun har én eneste oppgave.
I en av hans presentasjoner går han gjennom hvordan man deler opp en kodesnutt i et automatisk testrammeverk han har vært med på å lage. Den resulterende koden inneholder kode som ser sånn her ut:
if (isTestPage())
surroundPageWithSetupsAndTeardowns()
Dette er helt klart lett å lese. Men hva er det vi får ut av å lese det? Nesten ingenting! Det inneholder ikke noen kode!
Det forteller deg kun hva som gjøres. Men sier lite om hvordan.
Må vite hvordan
Tenk litt over hvorfor du er inne og leser kode. Det er gjerne fordi du vil endre på funksjonalitet eller legge til ny funksjonalitet.
- Du kaster bort alles tid med forkorta variabel-navn!
La oss si du skal legge til eller endre et felt i et registrerings-skjema. Hva er det du da trenger å finne ut fra koden? Du trenger å vite hvordan registreringen fungerer.
Når jeg går inn for å se på kode, så ser jeg først gjennom hvilke klasser/moduler som finnes. Og hvilke funksjoner disse har tilgjengelig. Dette er API-et til koden, og API-et bør helst vise intensjonen - altså hva som gjøres.
Dette er også alt jeg strengt tatt bør trenge å lese fra et eksternt bibliotek jeg bruker. Men for kode jeg selv skal forvalte, så trenger jeg å vite mer enn hva som gjøres.
Jeg er pokka nødt til å vite hvordan det gjøres også.
"Kan bli gal av mindre"
«Kode skal kunne leses som en god bok» har det blitt sagt.
En god bok har en innholdsfortegnelse - med oversikt over kapitler og kanskje underkapitler. Dette er viktig for å kunne finne frem i boka, og vite hva den handler om, og hvordan historien er bygget opp.
«Man må hoppe fra funksjon til funksjon til funksjon til en til slutt ikke lenger husker hva i huleste det var man lette etter.»
Men når jeg går til kapittel 5 fordi jeg har lyst til å lese den delen av historien, så blir jeg ikke særlig imponert hvis kapittel 5 kun inneholder enda en innholdsfortegnelse!
Hvis jeg må gå fra innholdsfortegnelse til innholdsfortegnelse til innholdsfortegnelse kaster jeg fort boka i søpla, og skriver gjerne en litt syrlig review på Amazon som en advarsel til potensielle kjøpere.
Altfor ofte er «god kode» akkurat slik. Man kan bli helt gal av mindre.
Man må hoppe fra funksjon til funksjon til funksjon til en til slutt ikke lenger husker hva i huleste det var man lette etter eller hvordan man kom seg dit.
Jo lengre ned i funksjonskallene man kommer, jo mer generiske blir de også. Koblingen til domenet man faktisk jobber med blir mindre og mindre klar.
Konsekvensene av endringer blir mindre og mindre tydelige, samtidig som de også blir mer og mer alvorlige - da sjansen for at koden blir brukt andre steder også øker.
"Jævla rebusløp"
Hvis du vil at koden din skal være lett å lese, ikke hakk den opp i tusenvis av deler og gjem den vekk spredd over alt, der eneste mulighet til å få den tilbake er å løse et jævla rebusløp der hver post refererer til 3 andre poster, som alle refererer til 3 andre poster igjen og igjen inn i evigheten.
Jeg har sett på min 20 år gamle kode, og gremmes 🙈
Når jeg skal endre på registrer bruker-funksjonaliteten for å legge til et nytt felt for samtykke, så har jeg lyst til å finne en tilhørende funksjon som heter registrer_bruker som tar inn en struktur med parametre. I denne funksjonen har jeg lyst til å se hvordan dataene i strukturen blir prosessert og lagret.
Alt trenger ikke være i samme funksjon naturligvis, men det er grenser for hvor mange «hopp» jeg skal måtte gjøre før jeg forstår implementasjonen.
99 prosent av programmerings-oppgaver er ganske enkle å implementere når det kommer til stykket. Likevel klarer vi lage helt vanvittig mye kompleksitet for å løse dem. Så slår vi oss på brystet over hvor smarte vi er som klarer å forstå kompleksiteten.
Dette driver vekk mindre selvsikre programmerere (jenter er typisk mindre selvsikre, for eksempel) ved å få dem til å tro at programmering er altfor vanskelig til at de kan gjøre en god jobb.
I enkelhetens navn
Ironisk nok innfører vi denne kompleksiteten nettopp i enkelhetens navn. Vi skal gjøre det enkelt å gjenbruke, for eksempel. Eller enkelt å lese hva som gjøres. Eller enkelt å kunne bytte ut implementasjoner.
Dette er alle ting som er bra å tenke på, det er ikke det.
Dette kan du lære av sterkt typet funksjonell programmering
Men de går så altfor ofte på bekostning av det som er viktigst av alt: At det skal være lett å forstå og endre på koden.
Gjenbruk kan være veldig fint, men det kan også bli et stort problem: Den gjenbrukbare koden er ofte vanskeligere å lese - ettersom den ikke kan inneholde nødvendig kontekst, og ofte må bruke generiske begreper og masse konfigurasjonsmuligheter for å kunne støtte alle brukstilfellene.
Gjenbrukbar kode er også mye mer risikabel å endre på. En endring vil kunne treffe mange flere case enn det du tilfeldigvis er inne og flikker på.
Som tekstforfatterne
Tekstforfattere er de som historisk har vært mest opptatt av lesbarhet. Og de er ikke så opptatt av gjenbruk.
De prøver ikke febrilsk å gjenbruke setninger og referere til andre bøker og kapitler når de skal beskrive noe. De skriver ikke sykt vage paragrafer med masse kompleksitet bare sånn at de kanskje kan bruke den samme paragrafen i neste kapittel også.
(Politiske rådgivere er kanskje ett unntak her, men de er heller ikke kjent for sine lesbare og oppklarende tekster.)
Gode, lesbare bøker inneholder kanskje en og annen fotnote og «se kapittel 3, side 45», men de inneholder først og fremst sammenhengende tekst man leser fra øverst til nederst.
«Kom igjen, folkens! Tør å vise litt kode i koden din! Ikke gjem den bort! Rebusløp kan du lage på fritiden.»
Gjør det enkelt!
Tilbake til koden til «Uncle Bob» i begynnelsen:
if (isTestPage())
surroundPageWithSetupsAndTeardowns()
Gikk man til isTestPage()-implementasjonen sto det bare:
public boolean isTestPage() {
page.hasAttribute("test")
}
Her vil det øke lesbarheten å inline funksjonen:
if (page.hasAttribute("test"))
surroundPageWithSetupsAndTeardowns()
Da ser man både hva som skjer og hvordan, uten å måtte context-switche til en annen metode.
Dersom if-statementen blir for vanskelig, kan man også flytte det ut i en egen variabel:
boolean testPage = page.hasAttribute("test")
if (testPage)
surroundPageWithSetupsAndTeardowns()
Ja, det blir en ekstra linje, men det hjelper så mye på lesbarhet å slippe å måtte context-switche ut i en egen metode for å forstå hva som foregår.
Kom igjen, folkens! Tør å vise litt kode i koden din! Ikke gjem den bort! Rebusløp kan du lage på fritiden.
Ja, det kan virke mindre imponerende å bare ha en klasse/modul som viser alt som skjer bak et gitt use-case. Men drit i å imponere - gjør det enkelt!
Lesbare og typesikre komponenter i TypeScript
Kristiane Alvarstein Westgård viser deg hvorfor du bør bruke union-types.