måndag 28 mars 2016

Ett case till på immutability

Detta kanske är något krystat, men när jag gjorde det slogs jag av att det sättet jag gjorde det på åtminstone påminde om principer runt immutable infrastructure. Ja, inte i alla delar, men det påminde om...

Vad gjorde jag? Jo, jag bytte ut min gamla router mot en ny. Detta utan att någon tjänst stod still mer än några sekunder och med möjlighet att rulla tillbaka föregående steg.

Så hur gjorde jag detta då? Eller låt oss först börja med varför jag valde att göra på detta sätt. Svaret på den frågan är att jag inte kommit åt administrationsgränsnittet på den gamla routern på flera år då gränssnittet bara fungerar IE7 som var typ antikt när jag köpte routern. Alltså kände jag att det fanns en risk att jag kanske bara skulle kunna några tjänster till den nya routern och låta någon eller några få ligga kvar tills jag klurat ut vad jag behöver göra ytterligare.

Så, hur gjorde jag...

Steg 1
Koppla in den nya routern till ström (duh) och kolla att alla inställningar med portnummerserie m.m är vad jag förväntar mig. Aktivera trådlösa nätverk.

Steg 2
Koppla in den nya routern som slav bakom den gamla så att den nya når internet genom den gamla via sladd.

Steg 3
Kontrollera att den nya routern når internet och flytta över de trådlösa enheterna som jag telefoner, chromecast, skrivare och bärbara datorer så att de når internet (och varandra) till den nya routern.

Steg 4
Här är steget jag var mest orolig över att jag inte hade koll på vilken information som faktiskt gällde. Men jag satte upp port forwards mot det fasta ip-nummer som min server skulle få i det nya nätet. Flyttade sladden från den gamla routern till den nya och bytte ip-nummer på servern.

Tekniskt blev det två steg i ett. Hade jag vetat hur man gjorde hade jag föredragit att ip-numret hade varit orört så att jag kunna flytta sladden mellan routrarna utan att behöva ändra något på servern. Men riktigt så bra blev det inte.

Steg 5
Fram med skruvdragare och skruva upp den nya routern. Naturligtvis förbannar man att man var duktig och buntade upp saker snyggt förra gången eftersom alla sladdar är på fel ställe, för korta etc etc.

Steg 6
Flytta internetet från den gamla routern till den nya då inga tjänster längre antas gå vi den gamla routern.

Så, i princip (om än inte helt) så gick varje steg i processen att backa till det föregående. Tjänsterna var inte helt ovetandes om att de flyttats men brydde sig väldigt lite om vilken router som de faktiskt använde. Dessutom var båda routrarna igång samtidigt utan att ställa det för varandra vilket torde vara det mest centrala kravet för att kunna räknas som en immutable leverans.

Bara en liten notering ur verkligheten.

tisdag 22 mars 2016

Ett case på immutability

Jag sitter och pillar med en presentation som jag eventuellt ska hålla någon gång i framtiden. Kanske skulle våga mig på att spela in den här gången.

Till denna presentation behöver jag ett case på hur en applikation kan utvecklas om man tar Open/Closed-principle lite mer på allvar.

Steg 1
En entitet, Person. Fälten id och namn.
En tjänst, PersonRepository. Fem metoder, create, read, update, delete och list100AfterId. De fyra första slänger en händelse, PersonUpdated. Sparar data i en relationsdatabas, tabellen Person.
Vi produktionsätter.

Steg 2
Vi upptäcker en brist i PersonEntiteten, det borde vara separata fält för förnamn och efternamn. För att inte förstöra för alla klienter till PersonRepository väljer vi att skapa en ny entitet, Person2 och ett nytt repository Person2Repository som sparar i tabellen Person2. Person2Repository använder list100AfterId från PersonRepository för att synka upp existerande data. För att fortsätta vara i sync lyssnar p2r även på PersonUpdated-händelsen.
Vi produktionsätter, allt fortsätter att fungera som tidigare. Vi kan bekräfta att den nya tjänsten fungerar som den ska och successivt skriva om klienter från den gamla tjänsten till den nya.

Steg 3
Ett behov av att kunna söka efter personer har identifierats. Då vi inte vill förändra i Person2Repository då det också skulle kräva ändringar hos tjänstens klienter väljer vi att skapa en PersonSök-tjänst.
PersonSök använder list100AfterId för att bygga upp ett index (Lucene, Elasticsearch, egen tabell i databasen eller något annat). Vi väljer att inte smälta ihop PersonRepository och PersonSok genom att integrera dessa i databasen. PersonSok lämnar alltid en lista av PersonId som svar på en sökning.
Vi produktionsätter. Det gamla rullar på, det nya kan verifieras och klienter kan snart börja använda.

Steg 4
Vi ska en tid senare implementera en tjänst som kan hitta personer som är nära dig. Vi kallar den för NäraDig. NäraDig använder sig av PersonSök7 som låter dig söka på fälten senastKändaPosition och returnera PersonId sorterat på tidFörSenastKändaPosition. Efter sökning hämtas Person-objekt från Person14Repostitory. Eftersom sök-tjänsten kan returnera många svar är vi oroliga för om Person14Repository orkar med den förväntade lasten. Därför implementerar vi olika anrops-scheman, ett anrop i taget, x synkrona anrop i taget och alla anrop på en gång. Vi implementerar en feature-toggle för att kunna ändra schema utan att behöva stoppa några tjänster.

Steg 5
Det visar sig att Person14Repository inte har tillräckligt goda prestanda för att NäraDig ska kunna användas och att kundnyttan är för dålig om man begränsar antalet personer som man hämtar information om. Man väljer att öka prestanda i PersonRepository.
I p15r byter man databas, egenskapen man söker är att man kan öka prestanda genom att lägga till fler noder av databasen. Man skriver dessutom om så att alla anrop till metoden read hanteras asynkront så att en instans av p15r kan hantera flera hundra tusen samtidiga anrop till read-metoden.

Steg 6
Person har nu efter ett antal "förändringar" blivit en trång punkt. Man väljer att lägga till nya fält för ofta, vilket leder till att övriga delar av systemet måste följa med (och synkronisering över flera versioner över lång tid är pita). Inom Person-entiten identifieras ett flertal objekt, namn, adresser, telefonnummer, epost, positioner, arbetsplatser etc, etc. Så nu har vi en mängd med entiteter med lika många repositories.

Steg 7
Det visar sig att förändringarna i steg 6 blir dyra att implementera då varje klient behöver skriva om all funktionalitet som berör Person till att hantera en mängd nya objekt. Så vi inför en ny tjänst, PersonAggregat. PersonAggregat samlar ihop all information om en Person som vi bröt isär i steg 6. Klienterna kan nu välja att bygga sina egna Person-objekt eller använda det färdigbyggda aggregatet.

Steg 8
Verksamheten vill kunna ta ut rapporter från det data som systemet innehåller, men har noterat att vi har minst tre datakällor, den vanliga databasen, PersonDatabasen som egentligen är 10 databaser och sökindexet, vilket ställer till det för det valda rapportverktyget. Vi tar fram en datamodell och använder list100AfterId-metoderna för att fylla en rapporteringsdatabas. Uppsidan blir att man kan köra så mycket rapporter man önskar utan att störa produktionssystemen.

Det jag tycker är intressant i dessa steg är att de aldrig kräver att klienterna måste vara klara att använda det nya. Det problemet blir en administrativ fråga där man får väga problemet med att synkronisera datakällor mot att kunna gå frammåt.

Att vi helt undviker att utföra joins i databasen och i stället väljer att ha en aggregat-tjänst som gör joinen mycket senare och som tillåter underliggande struktur att förändras är också värt att notera.

Detta är bara ett hypotetiskt case. Många frågor måste lösas. Tex om vi har flera versioner av en tabell, vilken är sanningen och hur upprätthåller man den? Eller hur ska händelser propageras på ett rimligt sätt?

onsdag 24 februari 2016

JFokus 2016

Jag var på JFokus 2016 och här kommer mina anteckningar och lite allmänna kommentarer. Jag gör ingen ansats att fylla ut anteckningarna till en ordentlig text, så ta det för vad det är, anteckningar.
Måndag
Chris Richardson - Introduction to microservices.
Elefanten och ryttaren, om hur vår hjärna fungerar.
The art of scalabiliy, bok
Det är olyckligt att de kända exemplen på microservices främst handlar om high-scalability.
Fred George, testning in production.
Microservices kräver agile och devops...
Antipatterns:
*Nano-service
*Distributed monolith

Aron Gupta - Docker & Kubernetes 
Inga anteckningar

Tisdag
Brian Goetz - Keynote
Java 10, immutable var det stora nya.

Holly Cummins -  Microservices: dream to reality
Don't unless you have devops.

Petter Måhlen - Modelling microservices at Spotify
Squad äger en "funktion" från DB till Gui.
System Z för att hålla ordning metadata
Scaling of the team drives microservices more than perforamace?

Bert Ertman - Microservices for mortals
Var inte naiv, du måste först vad du gör.

Ivar Grinstad - SnoopEE
SnoopEE kanske kan vara något för min nuvarande kund.

Kristoffer Erlandsson - Fault tolerant microservices
Relase it
Hystrix
Circuit breakers
Bulkheads
Har du inga nätverksproblem, mäter du fel.

Kathrine Stanley -Testning microservices
Inga anteckningar

Onsdag
Tim Berglund - Git from the bits and up. 
Läs Beowulf

Barush Sadogursky - Docker container lifecycles
Every plays with docker but noone gets to production
Docker images is just another artifakt, like jar-files.
The promotion pyramid. The promotion pipeline. Quality gates.
Build only once, including the Docker packaging. Only once. Only once, only once.
Arifactory metadata kan användas för att visa vilka qualitygates som passerats och dess resultat.

Rafael Winterhalter - Making java more dynamic
Använd agenter i stället för ramverk. Skriv plain old java applikations instead of EE, spring etc.
Bytebuddy

Manuel Bernhart - Reactive web applikation
(inga anteckningar)

Markus Esiele - CI Docker and JEE
Fabric8 kanske kan vara skoj om man vill gå redhat.

Sammanfattning
Devops är förutsättningen för microservices. Med egen drift eller molnet spelar ingen roll. Applikationsdriften sköts av utvecklingsteamet.
CD som en del av devops är förutsättning för microservices.
När du har CD och devops på plats, kan du börja med microservices.
Storleken på utvecklingsteamen driver Microservices lika mycket som prestanda.
Kunskap är viktigt, microservices kräver kunskap så att verkligen vara engagerad i ämnet är viktigt. Akta sig för hypen. Även om det finns verktyg så måste du känna till begränsningarna och fallgroparna.
Lite synd om Holly Cummins vars dator inte fungerade. Arun Gupta kändes trött och oinspirerad jämfört med vad jag sett tidigare.


fredag 12 februari 2016

En agil mjukvaruarkitektur

En agil mjukvaruutveckling brukar beskrivas som att man låter programvaran växa genom evolution i stället för genom en förutbestämd plan. Man hävdar alltså (korrekt) att det är svårt att i förväg förstå vilka konsekvenser beslut om en applikation får, förrän man faktiskt upplever hur det fungerar i "verkligheten", alltså när det är implementerat och klart.

Så, om du är agil så ska du göra fel, ändra, se att det fortfarande inte är bra och ändra igen. När du är klar med detta så ska du välkomna förändrade krav. Men i de projekt jag har jobbat i har man ansett att en utforskande ansats är för dyr, eftersom det är svårt att förändra när saker väl blivit skrivna. När kraven förändras väljer man ta långa omvägar för att det är så svårt att ändra i det man redan har. Med andra ord väljer man bort ett agilt förhållningssätt och lägger stor möda på att försöka att göra rätt på första försöket.

Jag ska nedan försöka beskriva några mönster och dess möjlighet till att göra det lättare att arbeta agilt. Dessa mönster kommer inte gratis på något sätt. Man kan se det lite som att med dessa mönster hoppas man på en komplexitetsökning som ser ut som O(log n) där den traditionella modellen något elakt skulle efterlikna O(n*n) (n i kvadrat alltså). Dock så börjar den agila modellen med en mycket större insats i kunskap och stödjande tjänster för att inte leda till katastrofens rand. Vet du inte vad du gör är det läge att låta bli och inte härma de som faktiskt vet vad de gör och varför.

Microservices
Med microservices väljer man att dela upp sin applikation i många små fristående delar i stället för en. Varje microservice blir simpel men man blir inte av med komplexiteten, utan den flyttar ut i nätverket där man hoppas att den ska gå och hantera på ett bättre sätt. Microservices stödjer det agila arbetssättet genom att du kan byta ut en microservices relativt enkelt utan att påverka resten av systemet och du är fri att ta beslut om hur din microservices ska fungera internt utan hänsyn till hur de andra är implementerade, kanske byta till ett mer lämpat programmeringsspråk, uppgradera bibliotek etc etc.

Prestanda brukar vara den brukar beskrivas som  drivkraften för microservices, men jag skulle hävda att möjligheten till att olika utvecklingsteam kan arbeta utan att påverka varandra är mycket starkare i normalfallet (Amazon och Netflix är inte normalfall)

Eventsourcing
Med eventsourcing sparar du ditt data utifrån vilka förändringar som har gjorts i systemet (verben) i stället för att lagra entiteter (substantiven). Man kan jämföra med en läkare som har sparat din journal (med alla undersökningar, prover och anteckningar) och använder den som grund för att ställa diagnos och väljer att inte ordinera jordnötter eftersom det står att du är allergisk i journalen till motsats till en akutläkare som ser att blodet sprutar och får utgå från det hen kan se just nu.

Med eventsourcing har du inte en modell utan snarare en modell per händelse och en modell per slutsats eller rapport du önskar skapa från dina händelser, även om du naturligtvis kan ha en övergripande modell någonstans i bakgrunden. Flera modeller gör det svårare att hitta hur ett enskilt attribut kom till världen men som å andra sidan gör att du kan anpassa dina modeller efter olika behov i stället för att anpassa behovet efter modellen. Attribut som tex rubrikfärg har jag sett många gånger krypa in i tex en person-entitet eller telefonnummer-entitet där man kanske hade varit mer betjänt av att ha en modell över presentations-stöd och en annan över domän-information. Så i stället för att ha en modell där en förändring påverkar all annan funktionalitet väljer man att kunna förändra en funktions modell separat från de andra. Notera att detta kan innebära en hel del dubbellagring av data och synkronisering av detta data måste ske på ett kontrollerat sätt.

Immutable
Att vara immutable innebär att man aldrig modifierar sitt data, man lägger bara till ny information till systemet. Det kanske låter som lite slöseri med hårddisk men det ger en del intressanta möjligheter, främst att du får möjlighet att titta på hur ditt data såg ut vid en tidigare tidpunkt. Som extra bonus blir cachning enkelt, eftersom den information som finns där aldrig förändras (det kommer ny information naturligtvis, men de gamla värdena är orörda).

Man kan dra immutable-begreppet längre än data och ta in begreppet immutable-everywhere. I princip innebär det att du aldrig ändrar i integrationer, du skapar en ny och den gamla får leva vidare tills det är säkert att ta bort den. Du ändrar aldrig i dina installationer på dina servrar utan gör en ny och låter den gamla leva vidare tills det är säkert att ta bort den gamla. Det ger bland annat möjlighet att skapa en ny integration till en ny klient som innehåller alla de senaste funktionerna, samtidigt som övriga klienter kan fortsätta att använda den gamla tjänsten tills de är redo att uppgradera. Drar man den tanken till sin spets får varje klient sin egen tjänst och kan där med förändras oberoende av andras behov.

Även vid serverdrift, särskilt i mer eller mindre virtuella sammanhang kan man välja att skapa en ny "image" som kan driftsättas parallellt med den gamla installationen och testas om den fungerar i produktion på en delmängd av trafiken innan man "pekar om" alla användare till den nya servern.

Automatisering
Människan är ganska dålig på att utföra repetitiva uppgifter och detta leder till ett kontrollbehov, eftersom vi kort och gott inte är att lita på. Detta leder till ett behov av kontrollstrukturer som i sig själva driver behovet av fler människor som ska utföra alla kontroller och i sin tur kontrolleras och ingen tillför värde för användaren av systemet (åtminstone inte direkt).

Vägen ut ur detta är automatisering. Efter att ha kontrollerat att automaterna(?) fungerar (tex gör rätt sak vid feltillstånd) kan de släppas lösa. Eftersom du kan ha ett större förtroende för att saker och ting fungerar kommer du också våga att förändra och förbättra i dina system. Detta leder till att ditt system blir större efter som fler uppgifter behöver programmeras och underhållas.

Sammanfattning
Att vara agil är så mycket mer än att ha en iterativ process. Det handlar också om att inte bara vara en döv, stum och blind idiot för vad kraven på systemet säger idag utan även för vad de kan komma att säga i morgon och förutsätta att saker kommer att förändras. Att placerat sig i en position där man kan välkomna förändringar även sent i projektet.

De mönster jag skrivit om ovan kommer inte gratis utan är dyra, både i tid och kunskap. De löser vissa problem men för med sig många andra som du också måste förstå innan du börjar, oavsett projektstorlek. Men när du börjar få problem med att du inte längre kan anpassa ditt system snabbt och smidigt kommer du önska att du tittat på ovanstående mycket tidigare.

torsdag 4 februari 2016

En ny skön värld

Ibland får man uppenbarelser, en känsla av att man förstått något och nu vet hur en aspekt av världen fungerar och varför det är som det är. Allt är självklart och nu är det dags att agera och inte svamla något om kanske och eventuellt.

Professionellt som programmerare är det ganska långt mellan varven som jag upplevt detta men nu de sista 1-2 åren har det hänt ganska ofta. Begrepp som
* händelsebaserat
* immutable
* functional
* reactive
* single responsibility
* continious delivery
* micro services
har allt gått som en blixtar genom mitt huvud.

Det är inte så att jag levt i en bunker isolerad från omvärlden. Det är inte nytt, men en förståelse för vad de faktiskt innebär och vad var de hör hemma har kommit till mig (hoppas jag i alla fall).

Den senaste blixten i allt detta är insikten att ingen av dessa är fantastisk i sig själv utan att de tillsammans bildar en helhet. En ganska radikalt annorlunda helhet än den vi är vana med men ändå en helhet som tar sin utgångspunkt i reaktion. Detta kanske motställt till den traditionella modellen som åtminstone i praktiken fokuserar på planering och förutsägelse.

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.