11 okt 2018

Varför gör vi detta mot oss själva?

Blogg_varfor_mot_oss.jpg


För några veckor sedan upptäckte vi att vissa datum på vår webbsida  var felaktiga. Istället för att visa det riktiga datumet för en  kursleverans så visade vissa sidor alltid dagens datum istället.

Om det här bara hade varit ett godtyckligt datum på sidan så hade det  inte varit någon fara. Men det här var leveransdatumet för kursen, det  som satt just under den stora fina "BOKA"-knappen. Det vill säga, precis det datumet en prospektiv kund skulle dubbelkolla just innan de bokade. Ajdå.

(Jag vet att, allt annat lika, så är det förmodligen en bra idé att  låtsas att vi på Edument är någon sorts övermänskliga utvecklare som  aldrig introducerar buggar i vår egen kod. Men så är det inte, hur  mycket jag än må önska det. Vi begår misstag. Jag upplever också att det  finns gott om framgångshistorier därute som förvärrar survivorship bias  inom vår industri. Jag hoppas att läsaren uppskattar en berättelse om  ett (litet) fel, dess orsaker, och hur vi alla kan lära oss från misstag  som detta. En bra sak med att jobba på Edument är att jag kan vara  nästan helt säker på att advokater inte kommer och knackar mig på axeln,  och ber mig plocka ned den här artikeln för att den kan presentera oss i  dålig dager.)

Tillbaka till buggen. Jag hörde med ett halvt öra när Cecilia  (produktägare för webbsidan) pratade med den femte-eller-så besökaren  den dagen som hade upptäckt att datumen var fel. Jag blev nyfiken så jag  kollade upp stället i koden som var ansvarigt för att spotta ut  felaktiga datum. Jag hamnade på den här raden kod:

<Date date={date.date} />

Date är en React-komponent som vi skrev själva för webbsidan. Inuti Date-komponenten delegerar koden klokt nog jobbet med datumformatering till moment(), en funktion i tredjepartsbiblioteket momentjs.

Min allra första tanke, när jag tittade på den här raden kod, var "jag slår vad om att date.date blir undefined på något sätt".

Funktionen moment() förväntar sig en sträng med ett  datum, men om den inte får en sådan, så kommer den att falla tillbaka på  "nu", det aktuella undflyende ögonblicket av datum och tid. Att skicka  in undefined som ett värde i JavaScript är moraliskt likvärdigt med att inte skicka någonting alls. (Situationen genljuder av Tony Hoares miljarddollarmisstag.)

Om buggjakten hade varit en brottsutredning och om jag hade letat  efter medel, motiv, och möjlighet, så skulle jag just ha hittat medlet.  Definitivt inte första gången i mjukvaruhistorien som mordvapnet visar  sig vara värdet undefined.

När jag tittade närmare på kontexten runt den kodraden, så såg det mycket riktigt ut som att date var en sträng redan, och därmed skulle date.date bli ett misslyckat uppslag (eftersom strängar inte har en date-property), och skulle resultera i undefined.

Vi kunde raskt peta upp en fix som ändrade date.date till date. Problemet var löst.

Men jag fortsatte att fundera på vad i vår process som hade släppt  igenom det här misstaget. Vi kodgranskar alla våra ändringar på  webbsidan. I det här fallet hade tre personer, jag själv inkluderad,  tittat på koden innan merge. Minst en person hade till och med kört  PR-branchen och testat den, och förmodligen tittat rakt på det felaktiga  datumet. Det såg ut som ett datum, så inga larmsignaler ljöd.

Det finns en annan fil i projektet med exakt samma kodrad.  Förmodligen hade den felaktiga raden blivit kopierad från den filen. Men  i den kontexten hämtades date-variabeln från en större datastruktur, och bara råkade vara ett objekt med en date-property. Samma rad var korrekt i den ursprungliga filen, men felaktig där den klistrades in.

Och det var här som saker gick fel, och ett brott begicks.

Människor gör ju fel ibland. Jag kan inte ens skylla på  själva klippandet och klistrandet, eftersom det var en vettig, praktisk  sak att göra i det här fallet att bara gå och hämta ett existerande bruk  av Date-komponenten, klistra in den, och vänta sig att den funkade på det nya stället. Den felaktiga förväntan var att variabeln date i den nya miljön betydde samma sak, vilket den inte gjorde...

...men vid den punkten gjorde vår utvecklingsmiljö ingenting för att påpeka detta.

I en tidigare bloggartikel "Tandborstning och statiska kontroller"  ställde jag den retoriska frågan "Varför gör vi detta mot oss själva?".  Varför slänger vi bort all kontextuell information, alla små bitar  kunskap om formen på data som vi skickar runt mellan olika delar av  programmet, bitarna som skulle hjälpa oss mest att undvika inkonsekvent beteende inom programmet som det vi fick den här gången?

Jag ville skriva den här artikeln för att jag kände att date.date-incidenten  är ett bra konkret exempel på någonting som hade kunnat fångas genom  att behålla mer typinformation. Mer specifikt, följande hade räckt för  att rädda oss i det här fallet:

  • Använd TypeScript.
  • Typ-annotera våra parametrar. Parametern date skulle ha typsatts som string.

Detta skulle ha räckt för att ge oss röda understrykningar under date.date, och buggen hade aldrig gått ut i produktion. "Property date does not exist on type string", hävdar TypeScripts typkontroll. Den har rätt!

TypeScript underlättar utvecklingen på exakt det här sättet, och  stöttar up en med hundra små detaljer varje dag. Det är både en  essentiell hjälp, och en påminnelse som gör en ödmjuk inför hur  begränsade vi är när vi utvecklar, alltid ett steg från kanten av en  ravin. JavaScript, däremot, är som en vän som först uppmanar en att  klättra upp för berget utan någon säkerhetsutrustning, och  sedan bara ler gåtfullt när man ofelbart trillar över kanten. Det är  häpnadsväckande att vi kan få någonting gjort i JavaScript.

Det finns en risk att hela den här artikeln ger intrycket av att vara  dogmatisk och ensidig. Jag inser förstås att valet mellan JavaScript  och TypeScript inte alltid är så svart-på-vitt, och det finns många  faktorer inblandade. Dessutom, sedan många decennier, har jag betraktat  mig som att jag är i det "dynamiska" lägret mer än i det "statiska"  lägret... vilket gör mig förvånad när jag märker att TypeScript med dess  statiska kontroller verkar funka för mig. Det lyfter min kodkvalité  utan att begränsa hur jag använder JavaScript, och utan att få mig att  klättra på väggarna med drakoniska typsystemsregler. Ett dynamiskt språk  med statiska kontroller — den bästa av två världar.

Så, kommer vi att lyfta över vår webbsida till att använda  TypeScript? Ja... så småningom. Med begränsade resurser finns det  fortfarande mer brinnande saker att adressera. Men det finns med på vår  lista av förbättringar att göra.

Relaterade kurser

  • TypeScript

    TypeScript tar avstamp från JavaScript och gör det till ett säkert språk. Du får gradvis typcheckning, kodkomplettering, och statisk kontroll av hur din kod hänger ihop. JavaScript har aldrig haft det hårda skyddande skal som vi är vana vid i nutida språk. TypeScript agerar som ett programmerings-exoskelett, och ger dig säkerhet, uttrycksfullhet och precision ovanpå JavaScript. 

    Kursområde: Webbutveckling
    Omfattning: 2 dagar
    Kostnad: 21 500 SEK