Alla dessa val av mönster

Vad är det primära som gör kod, gammal som ny, svår att förstå och därmed svår att förvalta och vidareutveckla? En utvecklare som har jobbat med två eller fler system i sin karriär har med stor sannolikhet suttit och kliat sig i huvudet och muttrat något i stil med "Vad är det som händer här, varifrån kommer det här värdet?"

I den här posten tänkte jag återge mina tankar och funderingar kring design, struktur och arkitektur på både den skrivna och oskrivna koden. Vad är det som driver fram valen av dessa egentligen? Det kommer inte gå att ge några gyllene svar, men kanske ge lite input till hur man ska göra sina val.

Vad är egentligen ett designmönster?

Om man googlar "Software design pattern" och navigerar till Wikipedia-träffen, https://en.wikipedia.org/wiki/Software_design_pattern, så får man en ganska bra definition på vad det är. Det är väldigt lätt att man förväxlar designmönster med struktur och arkitektur. Det spelar väl egentligen ingen roll så länge man har kontroll på vad man menar och inte blandar ihop begreppen. Till designmönster hör t.ex. inte S.O.L.I.D, som istället är en uppsättning principer för att hålla sin kod stringent.

  • En princip är ganska lokal medan
  • ett designmönster är lite mer regionalt

för att komma med någon slags liknelse från vardagen.

Vad är egentligen en arkitektur?

I likhet med ovan kan man googla "Software architecture" och då får man bland annat träffar som handlar om Microservices, Event-driven, Serverless etc. Detta tyder på en större utbredning.

  • En arkitektur är mer global för applikationen eller för systemet

Arkitekturen inkluderar i vissa fall även applikationens eller systemets omgivning. Även här är det lätt att förväxla med designmönster eller principer och även här spelar det egentligen inte så stor roll så länge man håller ihop begrepp från arkitektur och designmönster.

Hur ska man välja?

Att svara på den här frågan kan man inte göra på något annat sätt än väldigt generellt. Oavsett om man talar om val av arkitektur/struktur eller designmönster så bör man väga in många olika parametrar.

Vi återkommer till parametrarna snart och fokuserar till en början på NÄR man ska välja bland dom olika mönstren som finns därute.

När ska man välja och vad händer om man väljer "fel"?

Här känns det som att det bara finns ett bra svar och det känns ganska naturligt när man tänker efter en stund. Det gäller att göra dom stora valen så tidigt som möjligt i utvecklingsprocessen, vilket alltså medför att val av arkitektur bör göras tidigt, före val av designmönster och principer. Det här valet "täcker en stor del" av systemet eller applikationen, kanske till och med hela. Valet av arkitektur genomsyrar i dom allra flesta fallen hela den resterande utvecklingsprocessen.

När man börjar skriva koden så ställs man mer eller mindre direkt inför det faktum att koden måste struktureras för att den t.ex. ska vara enkel att navigera. Strukturen påverkar väldigt mycket hur man kan ta till sig kodbasen som presenteras i den utvecklingsmiljö som man använder. Valet av struktur påverkas alltså av IDE, använder man Visual Studio så strukturerar man koden utifrån en lösning med ett eller flera ingående projekt. Har man verktyg för refaktoreringar såsom namnbyten, utbrytning av kodblock med mera så kanske man inte behöver lägga mängder av tid på namngivningar. Detta kan undan för undan växa fram, men det viktiga under tiden är att försöka hålla stringensen, att inte måla in sig i ett hörn eller försätta sig i en massa problem med t.ex. cirkulära referenser eller snåriga och jobbiga beroenden.

Successivt växer nu kodmassan fram och blir större och större. Det är nu som man egentligen ställs inför dom svåra valen, dom små lokala valen. Vi börjar närma oss designmönster och principer.

Om man ser tillbaka "uppåt" så kan man misstänka att man gör:

  • Väldigt få arkitekturval under en utvecklingsprocess. Att ändra sitt val efter det att kod börjat skrivas är oftast dyrt och kan ses som en omskrivning av systemet från början

  • Flera val av kodstrukturen, eftersom dessa är mera lokala än arkitekturen och man gör alltså ofta flera val inom ramen av ett system och att ändra valen är mindre kostsamma

Som exempel på det senare kan man ta en SPA med tillhörande API-backend/BFF backend-for-frontend. Klientdelarna, ofta skrivna i javascript, kanske är en react-redux-applikation medan API:et är ett ASP.NET Core Web API. Att ändra strukturen i dom olika delarna är mindre kostsamt och borde kunna göras mer eller mindre oberoende av varandra, så länge man håller sig till överenskomna kontrakt.

  • Valen av principer är många flera. Att ändra mönster på den här nivån är ytterligare mindre kostsamt och ofta kan man luta sig mot enhetstester på den skrivna koden.

Nu är man alltså nere på val av mönster på enskilda moduler eller komponenter i systemet, man är nere på klassnivå när det gäller objektorienterade språk såsom C#, Java, C++ m.fl. och då handlar det kanske om S.O.L.I.D och andra principer.

Vad ska man välja?

Det här är nog den svåraste frågan att besvara. Den förändras såklart också med "upplösningen" på valet, alltså om valet är på arkitekturnivå eller designprinciper på kodnivå. Det som påverkar valet, oavsett nivå, torde vara minst följande:

  • Kompetens, kunskap och erfarenhet bland utvecklare
  • Förvaltningsbarhet, livslängd och framtiden för systemet
  • Arvet

Den sista punkten är väldigt intressant och kräver en lite längre diskussion och mer eftertanke före. Vi har mer eller mindre genom hela texten ovan fokuserat på att koden ska skrivas på ett eller annat sätt och att man bygger upp systemet inom vissa ramar. Vad har då arvet för betydelse? Det finns ju inget arv? Nej, så är det ju, men hur ofta är det på det här sättet? Jag vågar påstå att det är oftare man får en kodmassa till sig, ett system som är "färdigt", taget i produktion, som ska förändras, förbättras eller rättas. Då ställs man inför det ursprungliga problemet att på ett effektivt sätt ta till sig koden och hur den fungerar.

Tycker man att koden är svår att överblicka så kan det vara ett bra läge att refaktorera, se över principerna som är applicerade eller kanske införa om sådana saknas. Här spelar inte kodens lokala utseende jättestor roll. Om man däremot känner att man måste förändra på en högre nivå, regionalt, så spelar helt plötsligt arvet en betydligt större roll. Man kommer kanske stöta på problem med komponenters inbördes beroenden mm. Att gå från ett mönster eller struktur till en annan är mer eller mindre svårt beroende på vad man har för ingångsvärden, alltså vad man använt för mönster tidigare OM man har använt sig av något alls.

OK, vi har valt, vad händer nu?

Ni har kommit så här långt, ni har gjort diverse val, ett eller flera beroende på vilken nivå ni är på. Det är OTROLIGT BRA! Ni har alltså valt något, valt flera saker, ni har gjort aktiva val! Återigen, det är otroligt bra! I ett samtal med en arkitekt ute hos kund för en tid sedan namndroppades många olika mönster på många olika nivåer. Det var ett väldigt intressant samtal och vi var väldigt överens. Utan att tänka på vad vi ville komma fram till så hamnade vi till slut i följande:

  • Dom specifika valen man göra är nääästan sekundära, det viktiga är att valen är aktiva.

Det blev en väldigt intressant insikt för oss båda, tror jag. Att koden ser ut som den gör eller att systemet följer en viss arkitektur måste bero på något, det kan inte bara bli som det blir. Valen ska vara aktiva, det enades vi om att det var extremt viktigt.

Men, OK, vad händer nu? Dom aktiva valen är gjorda, är vi klara då? Svaret på frågan är för stunden ja, med tillägget att man bör:

  • Förvara valen någonstans. Detta görs med fördel tillsammans med källkoden.

Att skapa en README eller motsvarande i lösningen, eller kanske i varje ingående projekt, se till att den hamnar tillsammans med källkoden och att den versionshanteras är väldigt effektivt. Om man har en "aktiv" hantering av brancher, en strategi för versionering och releaser av källkoden så kan den också beskrivas här, tillsammans med källkoden i lösningen, i repot, i versionshanteringssystemet. Nu är du klar! Skriv kod för tusan!