lørdag 28. oktober 2017

Programmering er feilretting

Opp gjennom årene har jeg erfart at mange, kanskje de fleste, studentene som begynner å programmere får en nedtur når de oppdager hvor mange feil de gjør. Det kommer som en ubehagelig overraskelse for de fleste at de må bruke noen timer på få rettet feil og få ting til å virke tilfredsstillende. Mange tar dette som et nederlag og havner i en situasjon der de opplevere at dette er ikke noe for dem; dette får det ikke til. Jeg tror det er flere årsaker til dette, og vi har kanskje ikke hatt tilstrekkelig fokus på de faktorene vi har kontroll med.

Programmering en øvelse som skiller seg ganske mye fra det de fleste nye studentene er vant med. Det finnes ingen ferdig formel som skal anvendes for å løse problemet. Det er ikke slik at vi enten kan løsningen eller ikke. Det finnes dessuten vanligvis ikke noen forutbestemt løsning, noen fasit. I starten gis det vanligvis ganske konkrete oppgaver som skal løses, men det finnes nesten alltid mange måter å gjøre det på.

Denne åpenheten skal kombineres med å lære seg noen formelle rammer som vi må arbeide innenfor, syntaksregler. Denne kombinasjonen av formalia og åpne problemstillinger er en utfordring. Vi er trolig for lite oppmerksomme på arbeidsformen og det tankesettet dette krever.

Det er kanskje slik at de fleste av oss som underviser er for nøye når det gjelder å vektlegge formalia. Kanskje inflytelsen fra Dijkstra er større enn vi vil innrømme, se Programmering og matematikk. Det manifesterer seg ved at vi er for opptatt av å vektlegge det som er optimalt og korrekt. Objektorientert programmering er et godt eksempel. For de av oss som har programmert en stund er det finessene som opptar oss og som vi gjerne vil formidle(interface, abstrakte classer, arv, innkapsling, aggregering). Kanskje vi skulle skulle bruke mer tid på at et objekt i grunnen er en samling av metoder og data, og utvide begrepsapparatet som svar på problemer når de dukker opp.

Vi har en tendens til å demonstrerer løsninger som tilsynelatende er naturlige og opplagte når vi viser dem i forelesninger eller legger løsninger på nettet. Vi kan bruke ganske lang tid dagen før på å forberede et eksempel som framstår som naturlig og "smart". Vi hopper lett over de timene vi brukte for å få eksempelet til å fungere, og vi underslår en beretning om arbeidsprosessen. Det vi underslår er feilretting.

For et par års siden gjorde jeg sammen med kollega Harald Holone et eksperiment i forelesningene der vi brukte to dobbelttimer til å utfordre hverandre til å løse et programmeringsproblem i plenum. Vi fikk oppgaven på stedet og vi var ikke forberedte på hva som skulle gjøres. Hensikten var at vi skulle forsøke og formidle hva og hvordan vi tenkte når vi gikk løs på en oppgave. Hovedfokus i våre intensjoner var å formidle hvordan vi skulle planlegge løsningen, men vi brukte nesten all tid på feilretting. Jeg tror ikke verken Harald eller jeg var særlig fornøyde med det vi laget, men studentene var veldig fornøyde med å oppleve at vi gjorde feil og faktisk rotet ganske mye, samtidig som vi faktisk kom fram til (litt ufullstendige) løsninger.

Jeg vil argumentere for å betrakte feilretting som normalsituasjonen i programmeringsarbeid. Det er det vi bruker tid på og det er det som er den mentale utfordringen for nye programmerere. En av inspirasjonskildene mine til dette perspektivet er John Pirzigs bok "Zen and the art of Motorcycle Maintainance". Hans forhold til reperasjon og vedlikehold av motorsykler kan lett overføres til programmer. Det interessante med Pirsigs resonnementer er at de er satt inn i en større sammenheng som berører begrepet kvalitet og beskriver en vitenskaplig innfallsvinkel til feilretting. Det vil føre for alt langt å gå løs på en komplett analogi mellom motorsykler og datamaskinprogrammer, men det er noen punkter i feilretingsstrategien som vi kanskje kan ha nytte av å tenke gjennom.

Når det oppstår en feil eller en utilfredstillende effekt. eller mangel på effekt, må vi forsøke og finne en feilrettingsstrategi. Tilfeldige innfall og kjapp fiksing fører oss ofte ut i et enda større uføre. Jeg tenker ofte på den situasjonen jeg var i når jeg begynte å programmere. Som studenter skrev vi programmene på hullkort og leverte dem i bunkevis til en datamaskinoperatør som la vårt program i en kø, leste dem etter hvert inn og kjørte dem. Hvis vi var heldige (og operatøren ikke hadde mistet hullkortene i gulvet) kunne vi hente en utskrift etter to-tre timer. Hvis noe gikk galt kunne det være en dump med sidevis fulle av oktale tall. Dette førte til at vi tenkte oss ganske godt om før vi leverte programmet til kjøring. I dag tar en slik test mindre enn et sekund. Av og til tenker jeg at de to sparte timene for manges vedkommende forsvinner i planløs testing.

Noen av nøkkelbegrepene i Pirsigs feilrettingsstrategi er: Dekomponering, systematisk komponenttesting, og hypoteser.

Når han skal fikse noe på motorsykkelen er han omhyggelig med analysere vilke komponenter som er involvert og hvordan de henger sammen. For oss som programmerere betyr vel det enkelt sagt av vi må ha oversikt over funksjoner, klasser og biblioteker, og hvordan de henger sammen. Læringseffekten av en slik dekomponering er forhåpemtligvis at vi tenker gjennom feilrettingsscenariet når vi planlegger koden. Jeg vil tro at de fleste drevne programmerere gjør dette, bevisst eller ubevisst.

Komponenttestingen krever at vi har laget koden slik at vi kan utsette delene for systematisk testing. Vi må f.eks. kunne kalle en funksjon med parametere der vi på forhånd har en hypotese om hva som lager feil og hvilke feil. Vi forstår ikke hva som er galt hvis vi ikke kan etterprøve slike hypoteser.

Det interessante med denne tankemåten er at den er svært generell og at den kan bearbeides og betraktes som en vitenskaplig metodikk. Hypotesetesting blir en jordnær og praktisk arbeidsform, og er et tankesett som er anvendbart i mange av livets situasjoner, enten det gjelder fag eller ikke.

En annen side ved feilretting er den sinnstilstanden vi har når vi går løs på arbeidet. Vi må oppfatte det som en konstruktiv og normal situasjon. Kanskje vi til og med har laget koden slik at feilen er som forventet i en tidlig fase av arbeidet. Hvis reaksjonen på en feilsituasjon er en følelse av utilstrekkelighet og rådløshet er sjansen for å finne feilen ganske liten. Det er også slik at vi av og til er i en modus der vi føler at vi ikke har overskudd, eller ro til å grave oss ned i en testfase. Min og mange andres erfaring er at det hjelper å komme vekk fra skjermen og ta med seg problemet gå en spasertur. Det er av og til forbløffende hvordan problemer kan falle fra hverandre, dekomponeres, når vi får litt avstand til dem.

Noen feil er lette å oppdage:

og lette og rette

mens noen feil krever litt mer analyse

Vi kan aldri bevise at et program er riktig, og vi må være forberedte på at det vil intreffe situasjoner der det oppstår feil. Feilkilder kan være vår kode, oppståtte feil eller endringer i datagrunnlaget eller nettverket eller systemet programmet inngår i.

Min tanke er at et program i prinsipp er i en slik situasjon fra det øyeblikk vi begynner å skrive, og i hele programmets "levetid".