Toggle-systeem: Van ergernis tot feature

toggle
/ˈtɒɡ(ə)l/

Noun
– COMPUTING

a key or command that is operated the same way but with opposite effect on successive occasions.

Verb
– COMPUTING
switch from one effect, feature, or state to another by using a toggle.
“the play/pause button toggles between those functions”

In het werk van een developer wordt vaak gesproken over feature toggles (of flags): een manier om functionaliteit aan of uit te zetten. Volgens de bovenstaande definitie zou een toggle een simpele aan/uitknop moeten zijn. In de praktijk is dat vaak niet zo en zijn er ingewikkelde handelingen nodig om te togglen. Een jaar geleden besloot ik daarom een eigen feature-toggle-systeem te bouwen.

In mijn devOps-team bij de Rabobank gebruiken we ook (feature) toggles. In onze code zit een koppeling met een extern properties-bestand dat op een config-server staat. In de code lezen we een boolean property uit. Om van true naar false te gaan, moeten we het properties-bestand aanpassen en de gewenste waarde intypen. Aangezien dit een verandering van productiecode betreft, wordt er een pull request gemaakt. Nadat een collega het pull request heeft goedgekeurd en gemerged, moet de config-server een refresh krijgen. De nieuwe waarde van de property is daarna beschikbaar voor de applicatie, maar die moet hem nog wel inlezen. Dus nog even een herstart en we zijn eindelijk getoggled, hoera …

{ Herkenbaar? }

Volgen we de definitie van een toggle dan zouden we iets moeten hebben dat werkt als een lichtknopje: simpel, makkelijk in gebruik met een direct merkbaar effect dat even zo snel omkeerbaar is. Zelfs een Product Owner zou het moeten kunnen :-).

Ik begon over dit soort zaken na te denken vanwege ergernis over een jaarlijks terugkerende taak. Onze applicatie toont gebruiksvoorwaarden voor klanten, die ieder jaar vernieuwd worden. We laten collega’s zo dicht mogelijk bij 1 januari de juiste bestanden activeren (op 31 december om 23:00 bijvoorbeeld). Waarom konden we dit niet met één klik doen? Of inplannen zodat het automatisch gebeurt? We’re lazy developers damn it!

Zo’n jaarlijkse taak is het natuurlijk niet waard om een eigen feature-toggle-systeem voor te ontwikkelen – behalve dan dat het maken ervan leuk is. Het hebben van zo’n systeem zou er ook voor kunnen zorgen dat we het vaker gebruiken. Hoe langer ik erover nadacht, hoe meer taken met toggle-potentieel ik zag. Hoe ga ik dit aanpakken?

{ Killer features }

Het doel is natuurlijk om het werk van mij en mijn collega’s makkelijker en betrouwbaarder te maken. Om hen te overtuigen moest mijn app beter zijn dan de huidige oplossing. Makkelijk van true naar false kunnen togglen is de basis, maar dit is niet voldoende. Daarom bedacht ik de volgende killer features:

– Inplannen van een toggle

Ik wilde een datum en tijd kunnen selecteren waarop een toggle naar een gekozen waarde verandert.

– Canary testing: one canary, two canary, n-canaries

Om canary testing te faciliteren moest een toggle ingesteld kunnen worden om n keer true te geven, om daarna weer terug te keren naar false.

– Canary testing: percentage

Ik wilde ook het testen met een variabel percentage mogelijk maken.

– Regenerative Toggles

Het enige moment waarop je je druk zou moeten maken over de naam van je toggle, is tijdens het implementeren ervan. Het dashboard moet een afspiegeling zijn van de implementaties, zonder iets in het dashboard te hoeven doen. Als je een toggle per ongeluk uit het dashboard verwijdert terwijl hij in de code nog bestaat, moet het dashboard zichzelf uiteindelijk herstellen. Ik noem het: Regenerative Toggles … Bingo!

– Een toggle-dashboard

Via een duidelijke interface moet je de toggles kunnen aflezen, instellen, bedienen en verwijderen.

{ Fail fast }

There is nothing so useless as doing efficiently that which should not be done at all. – Peter Drucker

Het fail fast-principe zegt dat we zo snel mogelijk moeten stoppen als er iets fout gaat. Iets doen wat al gedaan is, vind ik erg fout, dus ik begon met de vraag: vind ik het wiel opnieuw uit? Ik vond mijn idee zelf natuurlijk uitermate origineel, maar daar kwam na een rondje langs verschillende collega’s en afdelingen snel verandering in. Onze Spring Boot-applicaties draaien in Pivotal Cloud Foundry (PCF) en de lokale PCF-evangelist vertelde me dat PCF geen passende oplossing had, maar vroeg “of ik al aan refresh endpoints had gedacht?”. In plaats van een herstart van je applicatie om je properties opnieuw in te laden, roep je slechts een endpoint aan. Het is standaard functionaliteit van Spring, maar wij kenden het nog niet. Bedankt Lars!

Nog lang niet tevreden zocht ik contact met verschillende (IT-support) teams. Daar bleken ze mijn plan een goed idee te vinden. Zo’n goed idee zelfs, dat ze nét een vooronderzoek naar Split.io als succesvol hadden bestempeld. Split is een enterprise-ready feature-toggle-systeem met uitgebreide mogelijkheden & metrics. Om mijn systeem nog enige kans van slagen te geven moest ik nu opeens zorgen dat het eerder in productie beschikbaar zou zijn dan Split.io.

Ik moet helaas bekennen dat ik er pas tijdens het schrijven van dit artikel achterkwam dat er verschillende opensource-toggle-projecten bestaan (bijv. togglz.org). Achteraf gezien had ik die misschien in mijn applicatie moeten gebruiken, maar dan had ik natuurlijk wel minder geleerd.

{ Alles kwijt? }

Vol goede moed startte ik met bouwen. De Spring Initializr maakt een Spring Boot-applicatie beginnen lekker makkelijk en ik doopte mijn applicatie AwesomeFeatureToggles. Voor minder dan awesome ging ik niet en het was ook de naam van mijn toenmalige team. Daarnaast bekt AFT ook zo lekker (pun intended!). Al snel maakte ik een endpoint dat false terug gaf. Zo had AFT binnen enkele minuten al de helft van de beoogde functionaliteit! Het begon pas echt ergens op te lijken wanneer ik ook true kon retourneren. Dus nóg een endpoint voor het togglen zelf. Daarnaast nog endpoints voor het verwijderen, inplannen en canary-testen. Ik maakte in Angular een product owner-proof dashboard. Daarvoor had ik nog een endpoint nodig voor het ophalen van een lijst met alle toggles. Al met al had ik een prettig ‘lekker bezig!’-gevoel.

Voor een applicatie is de implementatie van AFT simpel: een GET op het AFT-endpoint (toggles/{toggleId}/value) retourneert de boolean-waarde.

Vanuit het fail fast-principe en omdat ik graag collega’s meeneem in mijn plannen, besprak ik AFT geregeld met hen. “Goh”, merkte iemand op, “je houdt de toggles in-memory bij?”, “Klopt, in een hashmap, lekker snel!”, beaamde ik.  “Zeker, maar als AFT herstart ben je dus alles kwijt?”.

{ Milliseconden }

Oeps, daar had ik even niet aan gedacht. Om mijn collega’s te overtuigen AFT te gebruiken, moest het een herstart overleven. De oplossing is natuurlijk een database.

De database fungeert als backup voor de toggles. Wanneer AFT herstart, wordt de hashmap opnieuw gevuld vanuit de database en bij elke verandering aan een toggle wordt de database geüpdatet. Dit werkte prima in de testomgeving. In de productieomgeving zijn er echter twee van elkaar gescheiden zones. De database is wel gezamenlijk. Maar als ik in de ene zone een toggle veranderde en daarmee de database, had de hashmap in de andere zone nog de verouderde informatie. Ik moest dus elke verandering in beide zones uitvoeren tenzij ik een manier vond om ze te synchroniseren.

Wat nu als ik de toggle-waarde uit de database zou retourneren in plaats van de hashmap? Om dit te testen, bouwde ik nog een rechtstreeks-naar-de-database-endpoint en daarna gaf Chrome DevTools het antwoord. Het verschil bleek een orde van grootte: de hashmap gaf resultaat in 30 ms. De database in 300ms. Voor de MVP koos ik daarom voor de hashmaps met de database als backup. Synchroniseren kwam later wel.

{ Regenerative Toggles }

Zodra AFT een request krijgt voor een toggle waarvan de naam niet in de hashmap voorkomt, wordt de toggle aangemaakt met de standaardwaarde false. Vervolgens is de toggle beschikbaar op het dashboard. Verwijder je via het dashboard (per ongeluk) een toggle terwijl er nog een applicatie is die de waarde ervan opvraagt, dan verschijnt de toggle op zo’n moment dus weer op het dashboard alsof je hem nooit verwijderd had.

Als je een toggle wilt verwijderen moet je dat dus eerst doen op de plek waar het ertoe doet: in de code. Daarna kan je hem via het dashboard verwijderen en blijft hij ook weg.

{ Verantwoordelijkheid }

In AFT zorgt de eerste klant/gebruiker die door een applicatie gaat waarin een toggle-waarde wordt opgevraagd dus ook voor het aanmaken van de toggle. Je zou dus kunnen zeggen dat de ‘verantwoordelijkheid’ van het aanmaken daarmee bij de gebruiker wordt gelegd. Een collega vond dat dit niet de bedoeling was. De plek waar deze verantwoordelijkheid zou moeten liggen, bleek voer voor een discussie van bijna een uur. Ergens was ik het wel met hem eens. Tegelijkertijd had ik moeite met het opgeven van mijn idee. Gelukkig had hij veel geduld en zijn we uiteindelijk tot een compromis gekomen: ook via het dashboard zou je voortaan een toggle kunnen aanmaken.

De voornaamste overweging was dat het gebruik in een applicatie met weinig gebruikers zou kunnen leiden tot een lange wachttijd voordat de toggle eindelijk op het dashboard zou verschijnen. Door het handmatig aanmaken hoef je niet te wachten met het instellen of inplannen van de toggle en kan je ook de allereerste gebruiker een true teruggeven.

Bijkomend voordeel van de regenerative toggles is dat het minder secure collega’s helpt. Als je in het dashboard een toggle ‘foo’ aanmaakt terwijl je in de code ‘bar’ hebt gebruikt dan zal ook de ‘bar’-toggle vanzelf op het dashboard verschijnen zodra die voor het eerst wordt opgevraagd. Je hoeft dan dus niet meteen de code in te duiken om de naam aan te passen en opnieuw te deployen.

{ Draagvlak }

Na al deze moeite moest de applicatie natuurlijk wel gebruikt gaan worden door mijn collega’s. Want iets wat elke start-up zal beamen: tussen zeggen dat je iets tof vindt en het daadwerkelijk gebruiken zit een wereld van verschil. Hoe krijg ik mijn collega’s zover? Vanaf het begin heb ik mijn teamgenoten betrokken bij de problemen waar ik tegenaan liep. Ze kenden AFT dus al enigszins. Ik heb ze daarnaast ook gewoon op de man af gevraagd: “Als ik dit in productie zet, zou je het dan gebruiken?”. Als dat niet zo was, vroeg ik wat daarvoor nodig zou zijn en bouwde dat.

Een keuze waarvan ik juist dacht dat het zorgen zou wegnemen, heb ik daarom later teruggedraaid. Zo had ik ervoor gezorgd dat een fout rondom AFT (van een bug in de implementatie van een toggle, tot een bug in AFT zelf) altijd een false zou retourneren. Het grote voordeel is dat je applicatie blijft werken, al dan niet met de gewenste functionaliteit. De fout zou je kunnen loggen en daar een alert op zetten. Dit week nogal af van het fail fast-principe en kwam voort uit mijn onzekerheid: ik wilde nergens per ongeluk een crash veroorzaken. Ergens geloofde ik misschien niet dat het allemaal zou (blijven) werken? Mijn collega’s waren het er niet mee eens: “Gebruiken we een extern feature toggle-systeem, dan gaan we er ook voor: faalt er iets, gooi dan gewoon een exceptie”. Zo gezegd, zo gebouwd.

Een andere ‘techniek’ had ik juist beter wat minder kunnen gebruiken. Tijdens de gebruikelijke Agile-rituelen zoals standups en refinements werden alle problemen met onze software besproken. Ik riep vaak dat een feature-toggle de oplossing zou kunnen zijn. Zo vaak, dat ik op een gegeven moment de opmerking kreeg dat ik een nieuwe hamer had en nu overal spijkers zag. Misschien moet ik dus nog wat werken aan mijn growth hacking-skills.

{ Vuurdoop }

Hoe dan ook, het moment suprême naderde: op 31-12-2020 zouden er weer overeenkomsten moeten worden klaargezet; de ontstaansreden van het hele project. Hoe mooi zou het zijn als het nu, een jaar later, daarvoor ingezet kon worden? Ik was dus enorm blij en trots toen ik op 16 december 2020 dit bericht ontving:

En, op 1-1-2021 bleek: hij deed het! Hey Split, I won!

 

Maarten Koller is Software Engineer bij Ordina. Hij verdiept zich graag in teamcultuur, verbeterprojecten en Flutter.

 

Linkjes

Awesome Feature Toggle service: https://github.com/MaartenKoller/awesome-feature-toggle-service

Awesome Feature Toggles (Dashboard): https://github.com/MaartenKoller/awesome-feature-toggles