Naar kennisoverzicht

Continuous delivery bij SnelStart

Bij SnelStart ontwikkelen we boekhoudsoftware voor het midden en klein bedrijf. SnelStart doet dat al meer dan 30 jaar door middel van een desktopapplicatie en sinds kort ook met een online variant. Ongeveer drie jaar geleden zijn we bij SnelStart begonnen met de ontwikkeling van ons service platform. Dit is een Azure Cloud oplossing die onze klanten onder andere de mogelijkheid geeft om in te loggen vanuit hun desktop- of webapplicatie, online administraties aan te maken en belastingaangiften te versturen. Tot ongeveer twee jaar geleden was het releasen van dit platform moeizaam. In dit blog ga ik in op de veranderingen die we sindsdien hebben doorgemaakt. Hoe we van een dik releasedocument naar één druk op de knop gingen door middel van Continuous Delivery.

Voordelen van Continuous Delivery

Continuous delivery is een methodiek die er op gericht is om nieuwe features zo snel en efficiënt mogelijk in productie te krijgen. Voordat ik inga op hoe dit werkt, wil ik eerst kijken naar waarom we dat eigenlijk willen. De laatste jaren wordt dit onderwerp steeds actueler en dat heeft voor een groot deel te maken met de adaptatie van Agile ontwikkelmethodieken als bijvoorbeeld Scrum. Die methodieken stellen ons steeds beter in staat om de klant nauw te betrekken bij nieuwe ontwikkelingen en de klant daar vroegtijdig feedback op te laten geven. We signaleren daardoor snel of de ontwikkeling voldoet aan de verwachtingen van de klant. Wanneer dit niet zo is hebben we de mogelijkheid om op tijd in te grijpen en bij te sturen. Dit alles kunnen we echter alleen wanneer we ook in staat zijn die nieuwe ontwikkelingen snel en efficiënt bij de klant te krijgen. Het toepassen van Continuous delivery zorgt ook voor software van hogere kwaliteit. Doordat we veel zaken zullen automatiseren, waaronder het testen, worden fouten snel ontdekt. We kunnen dus vroegtijdig fouten herstellen en teams houden meer tijd over om zich te richten op het product. Een andere belangrijke reden voor SnelStart lazen we al in de inleiding: releasen was moeilijk en tijdrovend. Een veelgebruikte quote in het kader van Continuous Delivery is ‘If it hurts, do it more often’. Wanneer je iets vaker doet is de hoeveelheid werk die daarmee gemoeid is ook kleiner. Het doen van merges van code is daarvan een mooi voorbeeld. Hoe vaker je merged, hoe kleiner de merge is en hoe eenvoudiger hij is. Daarnaast krijg je eerder feedback op je proces waarbij je die beter kunt beheersen. Oefening baart kunst. Wanneer je iets vaker doet wordt je er simpelweg bedrevener in. Zaken die we meer dan eenmaal handmatig doen zijn een goede kandidaat om geautomatiseerd te worden.

Continuous Integration

Continuous Delivery begint met Continuous Integration. Het is een software-ontwikkelmethode die van ontwikkelaars verlangt dat zij hun wijzigingen meerdere malen per dag inchecken in een gedeelde repository. Elke check-in wordt dan geverifieerd door het draaien van een build en het uitvoeren van automatische testen. Het voorkomt onder andere dat je veel tijd kwijt bent aan integratie van verschillende branches en bugs worden snel gespot.

Er zijn natuurlijk meerdere branching strategieën. Bij SnelStart zijn we inmiddels uitgekomen op maar één enkele branch. Lange tijd hebben we gewerkt met twee branches (en in uitzonderlijke gevallen met een featurebranch). De ene branch stond voor dev, de andere voor productie. Eens in de zoveel tijd werd er van dev naar productie gemerged en dat werd dan gereleased. Ondanks dat we erg veel op één branch deden hadden we dus nog steeds te maken met een vorm van uitgestelde integratie. Mergen van dev naar productie ging dan ook lang niet altijd vlekkeloos. 

 

 

Met veel ontwikkelaars op één branch werken, vergt een hoge mate van kwaliteit. Die kwaliteit kan op meerdere manieren worden gewaarborgd. Zo doen we aan code-reviews van alles dat wordt ingecheckt, staat alles onder test en werken we met gated-checkins. Die laatste zorgt ervoor dat er niks kan worden ingecheckt dat niet heeft gebuild of waarbij niet alle unit-testen succesvol zijn gedraaid. Helaas heeft de gated-checkin ook een nadeel; als ontwikkelaar moet je even wachten op feedback van de build. Het is dus erg belangrijk om de build snel te houden, 10 minuten is wel het maximum.

Feature flags

Zoals al eerder gezegd werken we nog maar op één branch. We stimuleren het inchecken van veel kleine changes omdat dit de kwaliteit verhoogt. Je krijgt dan immers vaak en snel antwoord op de vraag of jouw change nog goed integreert met de rest van de code. Dit zorgt er dus voor dat er veel features die nog niet af zijn toch ingecheckt zijn en daarmee op productie belanden. Het ene team wil zijn nieuwe feature al releasen terwijl een ander team nog bezig is met zijn feature. Om dit te ondervangen zijn zogenaamde feature flags geïntroduceerd. Dit is niks anders dan een bitje in de database die vertelt of bepaalde code wel of niet gebruikt mag worden. Deze flags implementeren we in de code op verschillende manieren. Zo kun je denken aan het wel of niet tonen van een knop in de UI om daarmee, vooral nieuwe, functionaliteit te verbergen. Een simpel if-else statement om te kiezen tussen twee blokken code of het injecteren van een andere class d.m.v. DI configuratie zijn andere manieren. Het introduceren van deze flags geeft ons nog een belangrijk voordeel. We kunnen klanten of groepen klanten koppelen aan de status van een flag. Zo kunnen we dus bepaalde functionaliteit voor een bepaalde groep klanten aanzetten. Dit geeft ons de mogelijkheid om nieuwe functionaliteit bij een kleine groep te testen. Zo ontvangen we vroeg feedback en kunnen we de functionaliteit nog bijschaven alvorens deze naar een grote groep gaat. Ook kunnen we de impact op onze systemen goed monitoren. Het beschikbaar stellen van functionaliteit is dus niet langer een kwestie van een deployment, maar van het omzetten van een bitje. Niet langer een keuze van IT, maar een keuze van de business.

Continuous Delivery

Continuous integration alleen is niet genoeg. Het levert ons software waarvan we weten dat het in ieder geval compiled en door testen heen komt. Onze klant heeft er echter nog weinig aan. Dit is waar Continuous Delivery ons verder helpt. De kerngedachte van de Continuous Delivery is om een ​​herhaalbaar, betrouwbaar en stapsgewijs verbeterend proces te creëren voor het vrijgeven van software. Het doel van Continuous Delivery is dan ook om een ​​constante stroom van veranderingen in productie te krijgen via een geautomatiseerd proces. Dit proces leggen we vast in de deployment pipeline. De pipeline breekt het proces van het leveren van software op in een aantal fasen.

 

Het doel van elke fase is om de kwaliteit van de wijzigingen te beoordelen, steeds vanuit een andere invalshoek. In de afbeelding hierboven zie je een deployment pipeline van één van onze producten. Geen deployment pipeline is gelijk, maar vaak zijn de fasen build automation, test automation en deployment automation goed te herkennen. Hoe verder een wijziging komt in de deployment pipeline, hoe langer het duurt voordat we daar feedback op krijgen, maar hoe meer vertrouwen we krijgen in de wijziging. Faalt een stap in de pipeline dan zullen we niet handmatig ingrijpen om die versie alsnog uit te leveren. We wachten simpelweg op de volgende release die wederom de volledige pipeline zal doorlopen. We willen namelijk een betrouwbaar en voorspelbaar proces, van begin tot eind. Een aantal punten in deze deployment pipeline zijn belangrijk. Zo willen we altijd precies dezelfde binaries releasen naar al onze omgevingen om er zo zeker van te zijn dat wat we hebben getest op acceptatie ook daadwerkelijk op productie beland. De enige uitzondering daarop is configuratie. Uiteraard zullen we wijzigingen moeten maken om bijvoorbeeld te verbinden met een andere database. Om ervoor te zorgen dat het hele proces, maar ook de pipeline zelf, betrouwbaar is zullen we op precies dezelfde wijze moeten releasen naar al onze omgevingen. Op die manier hebben we ook het releasen op zich al vele malen getest voordat we dat uiteindelijk naar productie doen. Daarbij is het ook belangrijk dat alle omgevingen kopieën van productie zijn om bij de release naar productie niet ineens tegen problemen aan te lopen als bijvoorbeeld een andere versie van IIS. Het gebruik van een cloud dienst als Azure is daarin natuurlijk ideaal. Het stelt je in staat om gemakkelijk omgevingen aan te maken op het moment dat jij ze nodig hebt. Het inzetten van de deployment pipeline is net zoals het ontwikkelen van software een iteratief proces. Stapje voor stapje automatiseer je meer en kom je dichter in de buurt van een volledig geautomatiseerde pipeline. Net als de software verbetert dus ook het releaseproces iedere dag.

Tooling

Automatiseren doe je uiteraard met tooling. Voordat wij met dit traject begonnen gebruikten we reeds TFS. TFS was prima als build-systeem, maar bood geen mogelijkheden om onze software verder te releasen. Na wat onderzoek, onder andere met Microsoft ReleaseManager (de voorloper van build vNext), kwamen we uit bij Octopus Deploy. Het is een webbased pakket dat geschikt is om onder andere ASP.Net applicaties te kunnen releasen. Het geeft goed inzicht in wat er momenteel op welke omgeving draait en geeft je de mogelijkheid om dat met één druk op de knop naar een volgende omgeving te releasen. Het uitvoeren van zo’n release betekent het doorlopen van een aantal, uiteraard volledig geautomatiseerde, stappen. Je kunt daarbij denken aan het aanmaken van een omgeving, het bijwerken van een database en natuurlijk het daadwerkelijk plaatsen van de software op de server. Ook het draaien van de acceptatietesten hebben we geautomatiseerd en opgenomen in onze deployment pipeline. In dit blog lees je meer over Octopus Deploy en hoe je daarmee een Asp.Net MVC applicatie kunt releasen.

Implementatie

De implementatie hangt uiteraard af van de huidige situatie. Zo is het toepassen van deze principes gemakkelijker wanneer je aan een nieuwe applicatie start dan wanneer je reeds een bestaande applicatie hebt zonder bijvoorbeeld unit-tests. Een goede start is eigenlijk altijd het implementeren van continuous integration. Introduceer builds, laat deze triggeren op elke check-in en draai de unit-tests. Spreek af wat er moet worden gedaan wanneer een build toch breekt. Een volgende stap is het introduceren van een otap straat. Doe ook dit het liefst volledig geautomatiseerd, in dit blog lees je hoe je dat bijvoorbeeld zou kunnen doen. Wanneer de infrastructuur staat kun je beginnen om de deployment pipeline in te richten en je applicaties geautomatiseerd uit te leveren. Het invoeren van continuous delivery kost tijd. Het is een proces waar ook zeker de tijd voor moet worden genomen. Bij SnelStart zijn we begonnen met één product en hebben dit langzaam uitgebreid. Nog bijna elke dag brengen we kleine wijzigingen en verbeteringen aan en zijn we op zoek naar manieren om het nog stabieler, efficiënter en sneller te doen. Toch is er al een wereld van verschil met twee jaar geleden. Destijds releaseden we nog één keer per 2 maanden met moeite naar productie, tegenwoordig is onze webapplicatie na een check-in binnen een uur beschikbaar voor onze klant.