Hvordan vi fikk høyere fart med hyppige prodsettinger og parprogrammering

I november 2021 hadde vi besøk av Terje Heen og Julian Ravn Thrap-Meyer fra NAV. De fortalte hvordan de prodsatte mange ganger om dagen. De brukte ikke annet testmiljø enn produksjon, de parprogrammerte alt, hadde sluttet med Pull Requests samtidig som de opplevde økt kvalitet. Wow.

Jeg jobber som utvikler i “Område Hverdagsøkonomi” hos SpareBank 1 Utvikling. Vi har stort fokus på modernisering av plattformen, og de siste årene har vi kunnet prodsette oftere og oftere, gjerne flere ganger i uka. Pull Requests er en sentral del av utviklingsprosessen. Akkurat som hos mange andre selskap.

Vi ble inspirert av NAV og ønsket å teste ut mer parprogrammering og hyppigere prodsettinger. Og vi hadde én oppgave foran oss som var perfekt for å akkurat dette.

Kjenner du deg igjen?

La oss ta et steg tilbake. Dette er ofte den klassiske måten man jobber med utvikling av et API:

  • Du jobber alene på en oppgave i branch som bare vokser
  • Mye manuell testing av HTTP-kall i ulike testmiljøer
  • Én egen testansvarlig. Du får en rapport på hva som er galt og fikser feilene.
  • Underveis ber du teamet om å ikke endre så mye på samme kodebase før man selv har merget til master, det blir så vanskelig å merge. Man blir mer og mer stressa for å bli “ferdig”.
  • Du sparer automatisk testing til slutt, og skriver bare tester på en brøkdel-som du også påstår er “den viktige delen”.
  • Pull Requesten inneholder mye diskusjoner og meninger, gjerne på smådetaljer som syntaks. Men lite om helheten, og den kan bare du.
  • Du er nervøs ved prodsetting. Endringen er med i en release. Denne er full av andre endringer også. Feilsøking blir vanskeligere.
  • Det feiler på flere ting ved prodsetting, du har ikke kontroll på hva og det er krise i teamet. Du har høyest puls, det er jo “din” kode.
  • I fremtiden kommer alle til deg hver gang det skjer noe i denne delen av koden.

Så er oppgaven din ferdig, koden er i prod og det fungerer. Du og teamet ditt blir hyllet fra nært og fjernt. Men i notepad har du en lang liste over ting som må forbedres.

Men nå vil produkteieren videre. “Det funker jo, kundene er fornøyd”, sier han. Er ikke det det viktigste? Du får høre at “forvaltning vil vi alltid ha, og det må prioriteres mot andre viktige ting”. Vi kan gjøre det senere.

Du taper diskusjonen. Resultatet er splitter ny teknisk gjeld. Hvordan kunne dette vært unngått?

Hva må til for å kunne drive med hyppige prodsettinger?

For å jobbe med hyppige prodsettinger, kan du ikke bare ha effektiv teknologi og prosesser i teamene. Du må også ha en leder som skjønner hva som skal til. En som har forstått at utviklere ikke er datamaskiner. Og at koden ikke er ferdig selv om funksjonaliteten tilsynelatende er det.

For å utvikle med høy fart og hyppige prodsettinger nytter det ikke bare å være dyktig ninja-utvikler.

En sentral del er at du ikke er redd for å endre og prodsette. Og skal man endre uten å være redd, bør man ha visse mekanismer på plass:

  • Kunne utvikle slik at du kan prodsette små endringer om gangen.
  • Mulighet for parprogrammering.
  • Holde god testdekning.
  • Raske bygg på byggserver, gode tilbakemeldinger på feil.
  • Enkelt å prodsette og rulle tilbake.
  • Bruk av feature toggling og gjerne soft release (canary deploys).
  • God logging, monitorering og alarmer

Det finnes mer avanserte deploy-strategier, og flere selskap i Norge har kommet mye lenger enn oss. Men vi kan vi sjekke av mesteparten her, og det holdt for å teste ut hyppige prodsettinger.

Utprøving av hyppige prodsettinger med parprogrammering i oppgaven “Endre kategori”

Transaksjoner i SpareBank 1 får i dag satt kategorier på transaksjoner basert på data fra vår maskinlæring. F.eks. at en transaksjon fra Spotify får kategorien “strømmetjeneste” under “ferie og fritid”. Dette bruker vi til å gi deg en fin inndeling av hva du bruker penger på.

I enkelte tilfeller klarer ikke maskinlæringsmodellen å finne kategori, eller så den feil. Vi ønsket at kunden selv skulle kunne endre kategori. Slik ville også maskinlæringsmodellen bli trent og funksjonaliteten bli bedre for kunder alle i fremtiden.

Dette skulle vi utvikle. Her har maksinlærignsmodellen ikke fanget opp at Adam og Eva er frisør, og kunden skal kunne endre selv

Oppgaven inneholdt masse utvikler-snacks:

  • Mikrotjeneste for behandling av kategorier sammen med transaksjoner
  • Persistering og caching
  • Kall på kryss og tvers av interne tjenester, bruk av maskinlæring
  • Algoritmer
  • Frontend-utvikling og design

Kodespråk: Kotlin og React

Hvordan la til rette for hyppige prodsettinger og parprogrammering

Geir Olav og Bård Kristian parprogrammerer på privat, dyrt nerdetastatur
Geir Olav og Bård Kristian parprogrammerer på et privat, dyrt nerdetastatur
  • Parprogrammering, gjerne med innblanding fra andre. Fysisk eller over Teams/Slack.
  • Delte opp funksjonaliteten i mindre deler. Og delte den så opp enda mer. Hver enkelt del ble prodsatt fortløpende, også deler som ikke var i bruk enda.
  • Endringer styres via “Feature toggles” på f.eks. fødselsnummer. Vi prodsatte fortløpende, men bare for oss selv på teamet. Ble utvidet til flere brukere ved behov.
  • Utviklet REST-endepunkter tidlig, men stubbet APIer, database o.l. som også ble brukt i produksjon. Slik kunne vi teste helt spesifikke deler av koden, i produksjon, uten at vi gik ut med for mye samtidig.
  • Litt kode om gangen, men alltid med kjærlighet og automatiske tester.
  • Ingen egne testere, vi testutvikler og tester selv konitnuerlig

Vi startet midt inni kodebasen

Det klassiske ville vært å starte med databasen. Lage det persistente laget, serviceklasser og endepunkter. Og så implementert endringer i andre APIer vi er avhengige av, og til slutt sydd det hele sammen og testet alt på en gang.

Men vi valgte å snu på dette.

Vi startet med å utvikle koden som modifiserer transaksjonskategorien, en liten del langt inni kodebasen. Vi erstattet baksystemer med stubs, til og med selve APIet som skulle lagre kategori-endringen. Og så prodsatte vi det, men bare for min bruker.

if( socialSecurityNumber.equals(“Asgauts fødseldsnummer”)) {
// Kall til den nye koden
} else {
// Kall til gammel kode
}

På denne måten fikk vi testet noe av kjernen av forretningslogikken umiddelbart og lærte fortløpende hva vi videre måtte implementere i andre tjenester.

Samtidig fikk vi endret kategori på en transaksjon i produksjon og fikk en visuell opplevelse. Og feedback-loopen på endringer var lynrask.

Vår første del av koden så noe sånn ut. Vi laget en ny klassse som modifiserte på transaksjonkategorier feature togglet og stubbet baksystemer
Første endring i produksjon hvor vi bare endret kategori på én transaksjon, men kun for min bruker, med stubbede data fra baksystem

Det smalt i produksjon med en gang

Selv med denne bittelille endringen smalt det i produksjon. Vi fikk meldinger om rar oppførsel på eksisterende sortering av transaksjoner. Altså på en del av koden vi rørte, men vi ikke trodde skulle feile. Dette var enkelt å oppdage, fikse og prodsette — fordi endringen vår hadde vært såpass liten og avgrenset.

Dette ble også betydelig enklere når vi hadde parprogrammert og begge hadde full kjennskap til endringen.

Ved å teste i produksjon fikk vi altså ikke bare testet vår endring isolert. Vi fikk også testet hvordan koden vår oppførte seg sammen med resten av produksjonsmiljøet.

Vi parprogrammerte på alle endringer

Vi jobbet på oppgaven i ca. en måned og endte totalt opp med over 50 prodsettinger før det var live for alle kundene våre.

Utvalg av commits som ble merget og prodsatt, alt parprogrammert

Mens vi jobbet ble vi stadig bedre på at hver fiks, små refaktoreringer, integrasjoner mot andre system, oppdatering av tester osv. ble til egne commits som vi merget og vi prodsatte, sammen. Her nærmer vi oss “ekte” Continious Integration slik det orginalt ble beskrevet av Kent Beck.

Legg merke til deploy8 i gult. Det ble mer naturlig å rydde teknisk gjeld underveis da vi ikke var midt inne i en kjempestor branch

Lokalt og så rett i prod?

Vi opplever nå en endring i måten vi jobber på. Og metoden har oppstått uten av vi aktivt har forsøkt å endre oss:

Vi har så lite kode som går ut om gangen, med så gode tester, at behovet for å teste i test-miljøer reduseres. Og ikke nok med det, vi starter faktisk nesten aldri opp appen lokalt, ettersom vi får så bra svar gjennom gode automatiske tester.

Vanlig prosess har blitt: Tester kjører lokalt →merger til master → bygg og test OK på CI Server →ruller ut i prod

Vi koder med hvilepuls. Vi stoler på tester, stoler på den gode gjennomgangen vi får med parprogrammering, stoler på alarmer. Så lenge byggserver sier OK så går det bare rett ut i produksjon, samtidig som vi opplever mindre og mindre feil.

Vi opplever veldig lite av “jeg har bare igjen å skrive tester”. Det er allerede gjort som en del av utviklingen fortløpende og holder nede teknisk gjeld.

Dette øker farten betraktelig og vi har kontroll.

Nå forstår vi hvorfor NAV kunne ta bort Pull Requests

Da vi parprogrammerte, ble all koden diskutert og reviewet fortløpende til minste detalj. Det Pull Requesten derimot ble brukt til, var infodeling til de andre og et verktøy for diff på koden for oss selv.

Tiden Pull Requesten lå før den ble merget ble betraktelig redusert. Det ble mer en teknisk formalitet for compliance vi måtte gjennom for å få koden til master. Vi markerte Pull Requester som vist under, merget umiddelbart når bygg ble grønt, og så kunne resten av teamet se på diff om m vil.

Opplysning til teamet at vi har samarbeidet, og at vi derfor godkjente Pull Request og merget uten å vente på andre

Skulle hele teamet godkjent alle Pull Requests hadde, farten gått betraktelig ned. Vi hadde også måte avbryte det andre teammedlemmer jobbet med.

Slik beholder vi fart og flyt i teamet. Kunnskapsdelingen skjer hovedsakelig gjennom parprogrammering og rotering på hvem som jobber sammen.

Langtidseffekten

Vi hører ofte at man må kode på hver sin oppgave for å bli fort ferdig. I SpareBank 1 Utvikling jobber mange team med Radical Focus metodikken. Da jobber teamet i ukessykluser hvor man mandag “commiter seg” til å bli ferdig med noe til fredag.

Men det store produktet Sparebank 1 Digitalbank blir aldri ferdig. Vi er ca. 160 utviklere som endrer på kodebasen hver dag. Vi bør tenke langsiktig og på helheten.

1% forbedring hver dag gjør deg 37 ganger bedre på et år

James Clear forklarer i boken “Atomic Habits” at en forbedring på 1% om dagen, vil gjøre at du får en forbedring på hele 37 ganger over et helt år.

Med hyppige prodsettinger og parprogrammering får man forbedringseffekten til Clear inn i software utvikling:

  • Små endringer gjør at man enklere kan endre retning og jobbe mer innovativt.
  • Kompetanseheving. Diskusjonene gjennom parprogrammering er som et uendelig kurs i programmering.
  • Ingen “min og din kode. Kunnskapen om kodebasen økes på tvers hele tiden
  • Teknisk gjeld tas fortløpende.
  • Teamsamarbeidet og pyskologisk trygget blir litt forbedret hver dag.
  • Kunden får kontinuerlige forbedringer på produktet.

Alt dette er fint, men hvordan kommer du i gang i ditt team?

Det ikke-tekniske er ofte det viktigste. Her er noen enkle grunnprinsipper:

  • Bryt ned oppgavene så godt du kan. Og så bryt dem ned enda mer. Og få dem ut i produksjon så fort som mulig.
  • Planlegg ukentlig med lav WIP (work in progress). Ha gjerne færre oppgaver enn antall utviklere, det vil gjøre det naturlig å jobbe sammen
  • I starten kan det være uvant å skulle gå sammen i par for å løse oppgaver. En måte å komme i gang på er å avtale par i starten av uken inntil man får parprogrammeringen litt inn i fingrene.
  • Stop starting, start finishing. Teamet, ikke enkeltpersoner, skal bli ferdige med oppgaver. Hjelp andre med å bli ferdige fremfor å starte på noe nytt selv.
Stop starting, start finishing, hold WIP nede. Simen spør hvem som trenger hjelp i stedet for å starte noe nytt selv
  • Gjør en retro etter noen uker på hvordan det har gått. Finn det som passer ditt team.

Lykke til!

NB! Snart kommer en ny artikkel om hvordan parprogrammering påvirker flyt i programmering.

Referanser
James Clear (2018) Atomic Habits: An Easy & Proven Way to Build Good Habits & Break Bad Ones. Random House Business Books

Continuous Improvement: How It Works and How to Master It av James Clear

Hver commit er en ny deploy til prod, del 1 av Terje Heen

Ikke nok et jævla statusmøte! av Marthe Slaatsveen og Thomas Allan Nygaard

Continuous Integration av Martin Fowler

TestDouble av Martin Fowler

--

--

Vi jobber med digitale løsninger hos SpareBank 1. Vi liker å skrive om det vi brenner for

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Asgaut Mjølne

Asgaut Mjølne

System developer at Sparebank1 Utvikling