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.

lördag 8 oktober 2011

Olika typer av xUnit-tester

Jag fick ett ett uppdrag av jobbet för någon dag sedan och har funderat lite på det och tänkte att jag skulle skriva ner lite tankar runt detta så att jag kanske kan komma ihåg det. Det vi ska försöka att åstadkomma är ett gemensamt tänk runt automatiska tester på xUnit-formen.

Som de flesta som har läst lite om tester automation vet är det mycket billigare att rätta ett fel direkt än om man väntar en dag, eller en månad. Tar det tid innan man rättar felet har man hunnit glömma mycket om vad det var man skulle lösa och hur man resonerade när man skulle lösa det. Koden som blir kvar blir ett dokument på hur det är löst men säger inte så mycket om varför du valde den ena av två lösningar. Det skulle bli hela romaner i kommentarerna om man skulle få med de typen av information.

Hur drabbar detta dina tester. Jo, det ger att du vill ha snabba tester som du kan se till att de körs i samband med kompilering. Det är då du vet vad du har gjort och vad det är du har ändrat. Det finns en regel som säger att du bör kunna köra åtminstone tusen tester på en sekund för att de ska räknas som snabba. Det innebär i de allra flesta fall att dina tester inte har tid att vänta på en sql-fråga eller webbservice-anrop.

Men ibland vill man trots allt testa sina integrationer och även få en "hela vägen" känsla för att det fungerar. De snabba testerna kommer att per definition bara testa små fragment av din applikation. Många små tester kan testa hela applikationen men om du inte börjar med en strikt tdd-approch kommer det målet vara väldigt svårt att uppnå och många gånger även väldigt dyrt. Det kan alltså vara idé att ha en uppsättning med tester som där det är tillåtet att testerna får ta tid. Dessa skulle förslagsvis köras på natten i en bygg-server.

Vi har alltså behov av två testbibliotek för våra xUnit-tester. Snabba och långsamma. Jag kan dock se behov av ett tredje bibilotek med tester och det är integrationstester. Med integration i detta fall menar jag den integration som inte kan fungera likadant i utvecklar, byggmiljö som i produktionsmiljö. Saker som skulle kunna separera ett test från de andra är att ett anrop måste göras över en vpn-tunnel och att detta inte går att lösa i de långsamma testerna. Alltså att ett manuellt arbete måste utföras vilket man inte kan kräva att det görs varje natt.

Så för att summera. Jag kommer att rekomendera att vi från nästa beslutspunkt har två obligatoriska xUnit-projekt i våra lösningar och i dessa organiserar våra tester. Detta för att kunna fånga så många fel som möjligt i tidigast möjliga skede utan att för den skull offra möjligheten till att ha långsamma "täcker allt" tester där de kan vara lämpliga. Om man önskar kan man naturligtvis ha flera av varje testtyp men det är testprestanda som bestämmer i första hand.

torsdag 12 maj 2011

Seven languages in seven weeks - Erlang

Det femte språket i boken Seven Languages in Seven weeks av Bruce Tate är Erlang. Ett språk som togs fram av Ericsson då inga andra var bra nog. Erlang har ett par designmål som skiljer det från andra språk. Det hanterar väldigt många saker parallellt. Språket är tar även ett annat grepp på felhantering som kort och gott går ut på att det är helt ok att en process dör ibland.

Erlang - dag 1
Erlang har fått en del inspiration från Prolog. Faktum är att man började med att modifiera Prolog för att få det resultat man önskade. Så mycket av syntaxen är relativt lik. Man har tuples, atoms. Men inga objekt.

Erlang är trots att det är ett högnivå-språk lämpligt att köra hårdvarunära då det finns stöd för att manipulera bits och bytes. Erlang dessutom ruskigt stora tal naturligt. Så många siffror du får plats med i minnet.

Precis som med Prolog använder man rekursiva strukturer för att lösa problem.

Erlang - dag 2
För en Java-utvecklare är annonyma funktioner inget nytt. Även om det skiljer en del mellan den objektorienterade approchen och den i Erlang. Dock blir det lite mer spännande i Erlang som som är dynamiskt typat. Funktionen du får kan ju vara vilken funktion som helst.

En styrka med annonyma funktioner är att du precis som i Ruby kan skriva en funktion som du kör på varje element i en lista och du kan enkelt återanvända dina funktioner på vilken lista som helst.

Erlang - dag 3
Dag tre går igenom de parallella verktyg som Erlang har. Principen är väldigt enkel och bygger på koncepten om Actors och Messages som vi sett tidigare. Det finns några inbyggda sätt att skapa processer och att skicka meddelanden till den kö som varje process är utrustad med. Sedan kommer processen att beta av kön ett meddelande i taget. Det är höggradigt asynkrona meddelanden som skickas så vill man åstadkomma synkrona meddelanden får man ta till lite trick, men i princip får man vänta på att man får ett asynkront meddelande tillbaka. I princip kanske man kan säga att alla applikationer man bygger i princip blir som en liten svärm med webservrar som löser var sin uppgift.

Felhantering i Erlang går i mångt och mycket ut på att låta saker dö. Tricket är att inte hantera något tillstånd. Alla funktionsanrop bär med sig all information den behöver och nästa anrop kommer, förutsatt att samma information skickas in, ge samma resultat. Poängen med detta är att det lättare att döda en process och starta den igen om något blivit sjukt än att försöka att hantera fel. Den nya processen kommer att ge samma resultat som den gamla så det är ingen som kommer att bry sig kort och gott.

Jag är lite besviken på kapitlet om Erlang. Jag känner att jag saknar kopplingen till verkligheten. Kapitlet kändes akademiskt. Jag förstår att tillstånd är något som är dåligt om man vill hantera många saker samtidigt men samtidigt så har de flesta applikationer jag träffat på små beroenden på vad klockan är, eller hur många som är inloggade. Var tar all denna information vägen i en Erlangapplikation?

Jag har en teori om den här boken. Den är inte till för att ge en översikt på ett antal språk så att man ska kunna jämföra dem och kanske välja ut ett för att lära sig "på riktigt". Bokens syfte är att lära ut funktionell programmering, men genom att sockra med Ruby som objektorienterat språk och Io som är likt javascript så märker man inte hur man sakta lär sig funktionsbaserat tänkande.

fredag 6 maj 2011

Seven languages in seven weeks - Scala

Dags för språk nummer fyra ur boken Seven languages in seven weeks. Nu blir det Scala. Ett språk som många ser som språket som kommer att Java som huvudspråk till JVM. En av fördelarna med Scala är att det är kompatibelt med Java. Har du 10 år av kod med dig från Java så behöver du inte börja med att skriva om den utan du kan återanvända dina favoritfunktioner från Scala.

Scala - dag 1
Första dagen med Scala var rätt trist, lite syntax varav det mesta känns igen från de flesta c-derivat. Så det var inte mycket som kändes nytt. Det som var lite intressant var att man valt att ha mixins eller moduler (ruby resp io) och för att förvirra lite kallar dem för traits. Jag hoppas på en roligare dag två :)

Scala - dag 2
Idag har vi gått igenom Scalas nyckelord var och val. Skillnaden mellan dessa är om variabeln som deklareras är mutable (kan förändras) eller inmutable (kan inte förändras). Genom att ha stora delar av sin kod inmutable kan man utradera mycket av de problem som objektorienterade språk har med parallell körning, tex över flera processorer.

En stor del av kapitlet ägnas åt Scalas collections. Dessa är i mångt och mycket också uppbygda runt inmutable objects. Tex om du lägger till ett objekt i en lista får du tillbaka en helt ny lista i stället för att behöva oroa dig för om din lista har förändrats på oväntat sätt. Jag uppfattar det som kanske något ineffektivt men förstår att vinsten kommer när man börjar arbeta med parallell bearbetning.

Scalas collections kan användas i vanliga for-loopar men tanken är att man ska använda dem funktionellt. Alltså finns det sätt att skicka in en funktion som ska köras på alla objekt tex på din lista.

Scala - dag 3
Under dag 3 visades den inbyggda xml-hanteringen där xml kan definieras som vilken variabel som helst.

Patternmatching är en funktion som lite kan jämföras med de logiska vilkoren i prolog.

Parallellism i Scala liknar till viss del den i Io då den använder actors och meddelanden.

Lite sammanfattning om Scala.
Jag uppfattar Scala som en utveckling av Java där man kunnat släppa kravet på bakåtkompabilitet som finns. Så att byta ut trådbaserad parallellsim mot actors. Införa inmutable objects och functionalism m.m. Trots allt detta nya så kan man ändå återanvända sina gamla javabibliotek. Det är ganska trevligt tycker jag.