tisdag 29 december 2015

Är transaktioner blott en historisk parantes?

Jag har haft förmånen att få jobba med folk med erfarenhet ett antal gånger, jag menar närmare 50 års erfarenhet av programmering. Det som jag ofta hör, när dessa erfarna personer beskriver hur de gjorde förr, är det vi försöker att åstadkomma nu. Vi har bara varit ute på en utflykt, en normaliserad och transaktionstät utflykt, men just bara en utflykt.

Min tes är att någon gång i samband med att vi fick tillgång till RDBMS så slutade vi hantera två viktiga aspekter av vår programvara, tid och felhantering.

När man förr tog en fil (eller två) som indata till sitt program körde sitt program och producerade en ny fil var det data man hade i praktiken immutable. Om man upptäckte ett fel i programvaran kunde man kör det uppdaterade programmet utifrån de gamla filerna. Om man sparade sina filer hade man ett arkiv över alla förändringar man gjort på sitt data. Tiden var kanske inte lika mycket "up your face" som den är i eventsourcing, men den fanns där. Att modellera med tid på ett normaliserat sätt är ingen barnlek någonstans, för beroende på vilket delsystem som körs har de olika och ofta motstridiga krav som är väldigt svåra att slå ihop.

Vilket leder oss till nästa del av min tes. Förr, när man hade kört ett jobb och kontrollerade om det gått som det skulle, då fick man hantera vad som skulle hända. Om något gått fel tex fick någon besluta om att köra jobbet en gång till, tillkalla utvecklaren eller kanske till och med börja om från en tidigare tidpunkt. Det är lite som att lägga ett meddelande på en kö, man får välja hur man vill hantera om något gått fel, tex göra omskick, låta meddelandet dö, eller notifiera tjänsten som skickade meddelandet. I vilket fall är detta något som måste hanteras.

I dagens system tappar vi bort tiden eftersom vi glatt skriver över och raderar värden i vår databas utan information om vem, när eller varför. Om något går fel gör vi en rollback och tappar all information om vad som gjordes. Smidigt många gånger, men våra system är inte så flexibla på att möta nya krav och vår kompetens i hur man hanterar olika problemsituationer är låg.

Med tekniker som eventsourcing och microservices behöver vi återigen lära oss att leva utan den alltid närvarande transaktionen och vi måste lära oss att hantera problemsituationer på fler sätt än att rulla tillbaka en transaktion.

tisdag 22 december 2015

Fördomar inom mjukvaruutveckling

Jag fick en gång lära mig att fördomar kommer ifrån att vi människor inte är tillräckligt smarta för att kunna hantera allt i världen individuellt, utan istället måste generalisera och begränsa den information vi hanterar. Så det är lönlöst att bekämpa fördomar, de är ett nödvändigt redskap för att vi ska fungera. Men det vettiga personer gör, regelbundet och ofta, är att ifrågasätta sina fördomar och sprida dem med stor försiktighet. Så i stället för att säga att "så är det" kanske man använder uttryck mer i stil med "i min erfarenhet" eller liknande.

Med detta intro tänkte jag utforska den som jag upplever den största fördomen inom it, nämligen att dubbelt är dåligt.

Sedan jag började med programmering har jag fått lära mig kod ska organiseras på ett sådant sätt att man inte behöver skriva samma logik två gånger. Det är egentligen inget att ifrågasätta om det gör korrekt, men i korrekt ligger haken. För hur identifierar man vad som är samma logik och vad som inte är det?

En tanke man kan ha i bakhuvudet när man ska eliminera duplicerad kod är att en dålig abstraktion är dyrare än duplicerad kod. Med det menas att om du har tagit ett beslut om att slå ihop funktioner som ligger utspridda i systemet till en enda, då bör du inte bara vara säker på att den kod du refaktorerar representerar samma logik idag, utan även i morgon. För hur ska du hantera att en funktion som är beroende på din funktion i morgon har önskemål på en liten variation i din funktion? Duplicera din funktion och modifiera kopian? Lägga till en if-sats?

Problemet omfattas av single responsibility pattern. Din kod ska bara ha en anledning att ändras. Så om ekonomiavdelningen vill se vissa attribut på en produkt som inte får visas till kund på webben (täckningsbidrag tex) kan det vara bättre att ha två olika produktklasser, anpassade för sina respektive behov och inte riskera att ändringar i en ände av koden påverkar något annat.

Mitt andra exempel på "dubbelt är dåligt" är runt lagring. Vi anstränger oss bland annat hårt för att normalisera våra databaser så att inget data ska finnas på mer än ett ställe och det är först när vi tvingas av prestandaskäl som vi bryter "dubbelt är dåligt" och då oftast genom att använda en cache.

Men precis som med duplicerad kod är det en god idé att ifrågasätta om den enda datamodellen är den rätta. Tänk en ansökan som ska gå igenom en process, där olika attribut i datamodellen är relevanta beroende på var i processen du är. Ansökan har kanske ansökta uppgifter, bekräftade uppgifter, beslutade uppgifter och ett antal uppgifter som är relevanta för alla tre stadierna. Alla attributen tillhör en ansökan och refererar till samma identitet så det är samma ansökansobjekt. Kanske det då kan vara bättre att ha tre separata objekt, som representerar sitt respektive tillstånd, trots att ett antal uppgifter då blir dubbellagrade, att bara de uppgifter du behöver är tillgängliga och de uppgifter som är brus är borttagna ur scopet.

När man börjar gruppera sin modell efter vad man ska göra kan man få en modell där samma objekt dyker upp flera gånger men med olika attribut beroende på vad man ska göra. Den stora vinsten med detta tänk är att man designar bort många missförstånd genom att eliminera möjligheten att använda attribut som inte är relevanta. Å andra sidan måste man snurra igenom och sammanfoga de olika varianterna till en rapport för att kunna se tex hur många objekt man har och vilket tillstånd dessa objekt är i då data inte är lagrat på ett enda sätt längre. Som jag skrivit någon gång tidigare, komplexitet försvinner inte utan du kan bara välja hur du vill hantera den. Men jag vågar påstå att anpassa objekt till situation är en möjlighet vi slagit dövörat för då vi varit rädda för att dubbellagra information.

lördag 19 december 2015

Om inget får ändras

Om man ska utveckla ett "modernt datasystem", nu i slutet av 2015 kan man inte undvika termen immutable.

De enda anledningarna till att bygga något med mutable state är prestanda givet några  specifika krav och ohejdad vana. Men faktum är att vi inte haft datorer förrän kanske de 10 (?) senaste åren där vi faktiskt kunnat hantera immutable state på ett sådant sätt att vi kunnat dra nytta av det till vardags. Vi har tex inte haft minne nog för att inte välja att återvinna det vid första tänkbara tillfälle och hårdvara har varit dyrt. Så, trots att det finns mycket forskning i ämnet har det bara varit akademiska övningar. I praktiken var vi tvungna att använda alla trick och möjliga optimeringar vi kunde och därav har lösningar och processer varit mutable.

Men idag med servrar där terrabytes av ramminne är och petabytes av långtidslagring är möjlig har vi en helt annan situation och dåtidens krav på optimeringar är idag är ofta kontraproduktiva. För som ni vet, optimeringens första lag: optimera inte förräns du faktiskt vet att det behövs, gäller fortfarande.

Det finns ett antal dåliga aspekter med låta saker ändra på sig i systemet och det spelar i sak inte roll om vi pratar om integrationer mellan system, installationer av system eller ett värde på ett fält i en klass. De ger alla problem med synkronisering och om du ändrar en del av systemet (oberoende var) riskerar du att ställa till det för en annan del av systemet. Och synkronisering är svårt, väldigt svårt och vi behöver inte ens ha ett flertrådat system för att få problem med mutable state.

Så, finns det någon bra lösning på detta som inte introducerar en massa ny komplexitet? Svaret är naturligtvis nej, men vi kan välja att hantera en annan komplexitet som vi tror att vi har större möjligheter att bemästra.

En sådan lösning skulle kunna vara att enbart lägga till information i systemet och aldrig inom ramen "den dagliga verksamheten" radera (inte ens uppdatera) existerande information. Ändra inte värden på objekt, skapa nya. Ändra inte i listor av värden, skapa nya listor, ändra inte i existerande tjänster, skapa nya tjänster, ändra inte i integrationer, skapa nya.

Med tekniker som immutable infrastructure så finns det ingen gräns för att "inte ändra, skapa nytt", utan det handlar mer om den mognad som din organisation har för vad som går att realisera och vilka steg som är möjliga för att förbättra situationen. Om det tar tex din driftsavdelning 4 veckor att sätta upp en " ny" server som kan köras parallellt med den gamla tills den gamla blivit utfasad, ja då har ni en resa framför er. Andra saker som att ta bort alla setters från dina klasser och bara initiera genom konstruktörer och factories kanske är ett enklare steg.

Vad blir vinsten med att bara lägga till ny information i ditt system? Du blir av med synkroniseringsproblemet. Bara en sådan enkel sak som att en yttre loop inte behöver vara orolig för att en inre loop ändrar på förutsättningarna för den yttre är värd mycket. I andra sammanhang tillåter oförändringsbara objekt en närmast oproblematisk cachning, för du behöver aldrig invalidera förändrade objekt för att något har ändrat på det, bara lägga till nya. Historik blir enkelt när du inte har skrivit över den gamla informationen.

Ja, så summa summarum, har du och stället du arbetar på inte börjat att ta till er ordet immutable på allvar i hur ni arbetar är det hög tid att göra det nu. Det finns mycket att läsa och ämnena är många, allt från funktionella språk till eventsourcing till docker containers. Men allt landar i att mutable state borde förpassas till historien, tillsammans med mänskliga computers, hålkort och goto.

Memory lane

För någon vecka sedan återupptäckte jag den här bloggen som jag började skriva i samband med att jag läste Robert "Uncle Bob" Martins bok "Clean code".

Ja, det blev några blogginlägg och jag drog igång Jönköping Developer Dojo i processen. Det var roligt att läsa igen och väckte en hel del minnen.

Det har runnit en hel del vatten under bron sedan dess så jag tänkte försöka att dela med mig av en del erfarenheter jag har tagit med mig sedan dess, om sådant som inte gått så bra och om sådant som har gått bra och förmodligen en hel del av sådant jag skulle vilja göra nästa gång jag får bestämma över hur saker ska göras.

Ord som  immutable, simple, complect (nej, inte complex), essensial complexity, monolit m m kommer säkerligen att dyka upp lite här och var.

Ja, nu får vi se hur detta går. En ambition har blivit nedskriven i en blogg. Återstår att se vad det blir för resultat.