Twee Belangrijke Oorzaken van een Slechte Codebase

We merken dat de oorzaak van de meest problematische codebases vaak terug te leiden is tot 2 fundamentele problemen:

  1. Gebrek aan formele coding guidelines.
  2. Een constante druk om features te leveren zonder voldoende refactoring, vaak ook zonder deftige geautomatiseerde tests.

Het Probleem van ontbrekende Coding Guidelines

Wanneer een team geen formele coding guidelines heeft, krijgt elke ontwikkelaar vrij spel om sourcecode te implementeren zoals zij dat wensen. Wildgroei dus. Ervaren ontwikkelaars zullen zich misschien proberen aan te passen aan de stijl van de bestaande code. Maar vaak zal er gewoon geen consistente stijl in de bestaande code te ontdekken zijn. Concreet heeft dit volgende gevolgen:

  • De inconsistentie leidt tot onduidelijkheid en verwarring (uiteraard).
  • Het maakt het inwerken van nieuwe teamleden moeilijker.
  • Het leidt tot 'technical debt'.
  • Het maakt refactoring moeilijker.
  • Het maakt interne code reviews eigenlijk onmogelijk.

Uiteindelijk leidt het tot verminderde productiviteit, langere timelines, meer bugs en hogere onderhoudskosten. Sowieso komt er niets goeds uit voort.

Goede coding guidelines gaan echter verder dan alleen stijl- en formattering. Ze leggen een standaard vast voor ontwikkelpraktijken die in lijn zijn met de waarden, doelstellingen en grootte van het team en de organisatie.

Wat Effectieve Coding Guidelines Zouden Moeten Bevatten

Effectieve coding guidelines moeten uniformiteit met praktische toepasbaarheid in balans brengen. Ze moeten prioriteit geven aan leesbaarheid en onderhoudbaarheid, terwijl ze erkennen dat enige complexiteit onvermijdelijk is.

We moeten ook aanvaarden dat we de wijsheid niet in pacht hebben. Wat we nu een goed idee vinden, blijkt mogelijk na verloop van tijd een vergissing te zijn. Hopelijk hebben we dan op z'n minst iets bijgeleerd.

Naast stijl- en formatteringsregels zouden uitgebreide guidelines het volgende kunnen aankaarten:

  • Teststrategie: Hoe en wanneer welke sourcecode testen. We merken dat zonder richtlijnen vaak hoofdzakelijk de gemakkelijk te testen code wordt getest. Uiteraard zou de focus eerder moet liggen bij foutgevoelige of kritieke code die wel eens gewijzigd wordt.
  • Versiebeheer: Wanneer committen we, hoe branchen we, hoe noemen we de branches, gebruiken we pull requests etc.
  • Logging: Wat loggen we hoe en wanneer en op welk niveau.
  • Beheer van de environments: En dan niet enkel de standaard development/test/production scheiding, maar ook: hoe vermijden we dat er per ongeluk externe production API's worden aangesproken vanuit dev/test, of dat al onze klanten een test email krijgen.
  • UI/UX-consistentie: Plaatsing van interface-elementen (zoals het consistent positioneren van "Cancel"-knoppen), modal vs inline, interactie met forms, tables, etc.
  • Toon van de communicatie: welke taal en toon gebruiken we in allerhande berichten aan de gebruikers, zoals notifications, e-mails en foutmeldingen.
  • Design: Consistente toepassing van kleuren, schikking, typografie en andere ontwerpelementen.
  • Gebruik van productiegegevens: Indien nodig, hoe doen we dit op een veilige manier te doen.
  • etc

Coding guidelines moeten worden behandeld als vangrails. Uitzonderingen zijn mogelijk wanneer nodig. Zijn er te veel of steeds terugkerende uitzonderingen dan is het misschien tijd voor een re-evaluatie.

We beschouwen coding guidelines als een levend document dat evolueert met het team, project en de veranderende technologie.

Zijn er nog geen coding guidelines in de organisatie? Dan is het beter te starten met een beknopt document met stijl- en formatteringsregels dan direct alles te willen coveren. Laat de documentatie groeien wanneer de nood er is, en wanneer duidelijk wordt wat gewenst of eerder ongewenst is.

Bronnen voor Coding Guidelines

Bij het ontwikkelen van je eigen coding guidelines hoef je het wiel niet opnieuw uit te vinden. Er bestaan veel uitstekende open-source guidelines die als basis kunnen dienen.

Enkele voorbeelden:

De Feature-Factory, waar nieuwe features de absolute prioriteit hebben

De tweede grote bijdrager aan slechte codebases is de onophoudelijke druk om nieuwe features "asap" te implementeren zonder dat er tijd is voor enige vorm van refactoring.

"Kun je snel X toevoegen?" of "We hebben tegen morgen een kleine wijziging nodig aan Y" lijkt op zichzelf misschien onschuldig. 100'en van zulke kleine aanpassingen die zich opstapelen zijn echter nefast en leiden tot 'technical debt'.

En technical debt werkt als een gewone financiële schuld: als je die niet aflost wordt die steeds groter.

Note: vanuit zakelijk oogpunt kan het soms nodig zijn om zo snel mogelijk een lijstje met features te kunnen afvinken, wat dan misschien voor technical debt kan zorgen. Er is niets mis met technical debt die gecontroleerd en bewust wordt veroorzaakt. Beter een winstgevende firma met technical debt, dan een failliete firma met de perfecte codebase.

Hoe Technical Debt Woekert

Technical debt groeit meestal aan volgens een voorspelbaar patroon.

Ontwikkelaars implementeren snelle fixes in plaats van volwaardige oplossingen. Dat doen ze om aan strakke deadlines te voldoen, hun managers tevreden te stellen, of op te scheppen over hun coding skills. Deze fixes stapelen zich op tot een labiel kluwen waarbij enige vorm van uniformiteit of logica volledig ontbreekt. De cyclus gaat verder met beloften dat "We het later wel opkuisen," maar "later" komt nooit. De technical debt stapelt zich verder op en wordt steeds lastiger om aan te pakken.

Naarmate de codebase complexer wordt, begrijpen de ontwikkelaars de code ook niet meer volledig. Elke aanpassing leidt tot steeds meer uitzonderlijke bugs die steeds moeilijker op te sporen zijn. Hier wordt je als ontwikkelaar echt niet vrolijk van.

Waarom Refactoring Wordt Uitgesteld

  • Het levert geen direct zichtbare functionaliteit op, het lijkt alleen tijd en geld te kosten. Niet-techneuten begrijpen vaak de meerwaarde van refactoring niet. (soms is er ook geen meerwaarde)
  • De toestand van bestaande sourcecode is niet altijd duidelijk wanneer men een tijdsinschatting maakt. Vaak merkt men de nood aan refactoring te laat, en voert het werk uit zonder refactoring. Hallo technical debt.
  • Angst om nieuwe bugs te introduceren. Zonder degelijke geautomatiseerde tests wordt refactoring riskant. Dit creëert een negatieve feedbackloop.
  • Soms is de kennis/kunde ook gewoon niet aanwezig om een refactoring uit te voeren, of is de situatie zo uit de hand gelopen dat niemand er nog wil aan beginnen.

Je moet ook niet refactoren om te refactoren. Als het stabiele code is waar nooit iets aan wijzigt, heeft een refactoring weinig meerwaarde.

Maar Technical Debt kan gesaneerd worden!

  1. Bouw refactoring in het proces in:
    • Neem 10-20% refactoring-tijd op in elke schatting/sprintplanning.
    • Bekijk ook wel degelijk de bestaande sourcecode bij een inschatting zodat je refactoring kan inplannen indien nodig.
    • Pas de "padvindersregel" toe: laat sourcecode beter achter dan je het aantrof. Voeg indien nodig geautomatiseerde tests toe.
  2. Wees duidelijk in je communicatie:
    • Verklaar technical debt aan niet-techneuten met behulp van herkenbare metaforen. Mijn favoriete: technical debt is als het fixen van een lek onder je gootsteen met een emmer in plaats van een degelijke reparatie. Het werkt voor nu, maar vroeg of laat loopt die emmer over, groeit er schimmel in je kasten, heeft je onderbuur waterschade en maakt die schimmel je ziek.
    • Probeer slechte code te meten en visualiseren in termen van bugs en tragere oplevering. Kun je laten zien hoe een deel dat geplaagd wordt door technical debt meer tester-developer cyclussen doorgaat? Of na het testen er alsnog meer bugs worden gevonden, bij steeds raardere edge cases?
    • Geef ook duidelijk aan wanneer refactoring nodig is. De stakeholders moeten er op z'n minst van op de hoogte zijn.
  3. Pak het stapsgewijs aan:
    • Focus eerst op gebieden met hoge waarde en hoge impact die geplaagd zijn door bugs of die regelmatig wijzigingen ondergaan.
    • Indien ze er nog niet zijn: implementeer sowieso geautomatiseerde tests vóór de refactoring en zorg dat ze werken met de huidige sourcecode. Probeer de tests volledig te maken: test niet enkel het 'happy path' maar ook zoveel mogelijk historische uitzonderingen/bugs.
  4. Integreer het in de cultuur:
    • Beloon kwaliteit boven pure snelheid. De ontwikkelaar die consistent nagenoeg bugvrije code produceert moet het voorbeeld zijn voor het team. Vaak valt deze echter minder op dan de cowboy/cowgirl die aan de lopende band nieuwe features implementeert die geplaagd zijn met bugs.
    • De mindset 'het moet niet direct juist zijn, de tester moet ook een job hebben' moet strict gecounterd worden. Er zijn teams die zonder manuele testers werken en er toch in slagen solide code te produceren. Het vangnet van een tester zorgt te vaak voor een lakse houding.
    • Is het voorzien van automatische tests nog niet ingeburgerd in het team? Start daar dan mee. Schakel eventueel een consultant in om samen met het team de eerste stappen te zetten indien nodig.
    • Succesvolle refactoring-inspanningen mogen ook gewoon benoemd, geregistreerd en gevierd worden, net zoals ontwikkelingen die het management of de klanten wel zien.
  5. Maak Technical Debt zichtbaar:
    • Voorzie code van commentaar/tags zoals NEEDSREFACTORING of NEEDSTESTING zodat men er bij schattingen en aanpassingen rekening mee kan houden. Het helpt ook wanneer iemand wat extra tijd heeft (gebeurt dit ooit in het echte leven?) om aan refactoring of automatische tests te werken.
    • Creëer een "Technical Debt Register" om problematische code en de impact ervan te registreren. Door daarbij wijzigingen, bugs en gefaalde tests te documenteren kunnen meer geïnformeerde beslissingen genomen worden over de aanpak van zulke probleemgebieden.

Conclusie

Coding guidelines en doelbewuste, tijdige refactoring komen een codebase altijd ten goede.

De voordelen op lange termijn zijn aanzienlijk:

  • Sneller inwerken van nieuwe teamleden.
  • Meer voorspelbare inschattingen en ontwikkelingstijden.
  • Minder problemen tijdens testing, snellere doorstroom naar productie.
  • Minder bugs en productie-incidenten.
  • Betere en snellere reactie op veranderende eisen.
  • Verbeterde tevredenheid en retentie van ontwikkelaars.

De kwaliteit van je codebase is een directe weerspiegeling van je ontwikkelingsproces.
Verbeter het proces, en de code zal volgen.

Twijfels over uw source code?
Wij brengen duidelijkheid.

Vrijblijvend gesprek?