Blog

Kies je stack voordat je weet hoe groot het wordt

Applicaties beginnen klein en groeien als ze werken. Waarom dat de frameworkkeuze bepaalt, en waarom overstappen later duur is.

Vrijwel elke applicatie die wij overnemen begon als iets kleins. Een intern hulpmiddel, een koppeling tussen twee systemen, een dashboard voor één afdeling. Niemand zat aan de tekentafel met de gedachte dat dit tien jaar mee zou moeten. Het werkte, dus het bleef. Het bleef, dus er kwam iets bij. En na verloop van tijd draait de bedrijfsvoering op iets dat is gebouwd voor een vraag die veel kleiner was dan de vraag van vandaag.

Dat is geen faalverhaal. Dat is de normale levensloop van software die succesvol is. Het probleem is dat de belangrijkste architectuurkeuzes worden gemaakt op het moment dat je het minste weet: aan het begin, toen het nog klein was.

De vraag die vooraf gaat aan de techniek

Wij beginnen een project niet bij het framework. We beginnen bij wie, wat, waar, waarom en wanneer. Wie gaat hiermee werken, en wie beheert het over vijf jaar? Wat moet het doen, en wat nadrukkelijk niet? Waar draait het, en onder wiens verantwoordelijkheid? Waarom bouwen we dit eigenlijk, in plaats van iets te kopen of het proces te veranderen? En wanneer moet het er zijn, en wanneer is het klaar?

Die vragen zijn niet technisch, maar ze bepalen de techniek. Een applicatie die drie jaar meegaat en dan wordt weggegooid, verdient andere keuzes dan iets dat de komende tien jaar de facturatie draagt. Een systeem dat door één persoon wordt onderhouden verdient andere keuzes dan een systeem waar over twee jaar een team van acht op zit.

Wij adviseren vanuit bedrijfseconomisch perspectief én technische haalbaarheid. Dat zijn twee verschillende dingen, en ze wijzen niet altijd dezelfde kant op. Het goedkoopste om te bouwen is zelden het goedkoopste om te bezitten. De rekening van een architectuurkeuze komt zelden in het eerste jaar.

Laravel en Symfony lossen verschillende problemen op

Laten we eerlijk zijn over wat deze frameworks proberen te zijn, want de discussie erover is vaak onnodig religieus.

Laravel is gebouwd voor snelheid van ontwikkeling en voor het plezier van de ontwikkelaar. Dat is een legitiem en waardevol doel. De documentatie is uitstekend, het ecosysteem is groot, en je hebt binnen een dag iets draaiends. Voor een afgebakende applicatie met een overzichtelijke levensduur is dat precies goed.

Symfony is gebouwd voor complexe systemen die lang meegaan. Expliciete configuratie, strikte scheiding van verantwoordelijkheden, een dependency-injectiecontainer die zichtbaar maakt waar alles vandaan komt. Dat oogt bij de eerste regels code als meer plichtplegingen, en voor een wegwerpscript is het dat ook.

Twee goede frameworks, twee verschillende doelen. Tot zover is er niets aan de hand.

Waar het scheef gaat

Het probleem ontstaat niet bij de keuze. Het ontstaat bij de aanname die eronder ligt: dat je aan het begin weet in welke categorie je project valt.

Dat weet je niet. Wat je weet is wat de opdrachtgever vandaag vraagt. Wat je niet weet is of dit over drie jaar het systeem is waar de hele operatie op leunt. En de statistiek is niet neutraal: applicaties die niet werken worden weggegooid, applicaties die wél werken groeien. De projecten die blijven bestaan zijn per definitie de projecten die groter werden dan bedoeld.

Dus de vraag is niet “wat is er nu handig”. De vraag is wat er gebeurt als dit een succes wordt.

Refactoren wordt onveilig

Wat een applicatie op leeftijd houdt, is of je hem nog durft te veranderen. Niet of de code mooi is, maar of je een naam kunt wijzigen zonder je adem in te houden.

Facades, globale helpers en runtime-resolutie zijn prettig om mee te schrijven en lastig om over te redeneren. Je IDE ziet niet wat waar gebruikt wordt. Statische analyse komt een heel eind, maar niet overal. Bij tienduizend regels merk je dat niet. Bij honderdduizend regels merk je dat je bepaalde delen van de applicatie niet meer aanraakt omdat niemand meer overziet wat eraan hangt.

Dat is het moment waarop een codebase begint te stollen. Niet met een crash, maar met een team dat eromheen begint te werken.

Meer mensen, meer manieren

Vrijheid in een framework schaalt omgekeerd met teamgrootte. Met twee ontwikkelaars is vrijheid snelheid. Met acht ontwikkelaars is vrijheid vijf manieren om hetzelfde te doen, en drie daarvan zijn inmiddels in productie.

Symfony is expliciet tot op het vervelende af. Precies daarom leest code van iemand anders er hetzelfde als je eigen code van vorig jaar. Dat lijkt een detail totdat je iemand moet inwerken op een systeem dat je zelf niet hebt gebouwd.

Het upgradepad bepaalt de levensduur

Software gaat niet dood aan bugs, maar aan een versie die niet meer wordt onderhouden en een upgrade waar niemand aan durft te beginnen.

Symfony heeft LTS-releases en een strikt deprecationbeleid: je krijgt waarschuwingen ruim voordat iets verdwijnt, en het upgradepad is een gedocumenteerd proces. Dat is geen glamour, maar het is het verschil tussen een applicatie die je in 2034 nog kunt bijwerken en een applicatie waarvoor je een herbouwbudget moet aanvragen.

Waarom terugkomen op die keuze zo duur is

Hier zit de kern, en het is het argument dat in dit soort discussies meestal ontbreekt: de overstapkosten zijn niet symmetrisch.

Beginnen met Symfony kost je in de prototypefase vrijwel niets. Hier komt meestal het tegenargument, en het houdt geen stand: een Symfony-project is één commando en een paar minuten, de defaults zijn prima, en je hoeft er niets aan te veranderen om te kunnen bouwen. Mijn ervaring is dat een prototype er net zo snel staat.

Later overstappen kost je een verbouwing.

Het zwaartepunt zit in de datalaag. Eloquent is Active Record: je model kent de database en de database kent je model. Doctrine is Data Mapper: je domeinobjecten weten niet dat er een database bestaat. Dat is geen kwestie van smaak maar van een fundamenteel andere verhouding tussen je code en je opslag.

Migreren betekent dus niet dat je query’s herschrijft. Het betekent dat je domeinlogica moet losweken uit je modellen: logica die daar in de loop der jaren terecht is gekomen omdat het framework dat aanmoedigt. Als je geluk hebt ligt er een repository pattern onder en heb je een aangrijpingspunt. Maar die garantie heb je niet, want niets in het framework dwingt het af.

Je kunt Eloquent meeverhuizen naar Symfony. Technisch kan het. De vraag is of je dat wilt, want dan neem je precies datgene mee waarvoor je aan het verhuizen was.

Ik heb legacy-migraties gedaan waarbij de datalaag zonder duidelijke structuur was gegroeid. Dat is een ander soort werk dan bouwen. Je besteedt het grootste deel van je tijd aan het achterhalen wat het bestaande gedrag eigenlijk ís, voordat je iets mag veranderen. Er is geen specificatie; de code ís de specificatie. Dat is te doen, maar het is traag, het is duur, en het levert de opdrachtgever op de dag van oplevering exact hetzelfde op als wat hij de dag ervoor had.

Dat is de rekening die je vooruitschuift als je de architectuurkeuze uitstelt.

De keuze is asymmetrisch, dus behandel hem zo

Zet het naast elkaar:

Kies je Symfony voor iets dat klein blijft, dan houd je een codebase over die iets formeler is dan nodig was. Dat is de hele prijs. Geen verloren tijd, geen tragere start: wat structuur die zich nooit heeft hoeven terugbetalen.

Kies je Laravel voor iets dat groot wordt, dan sta je over een paar jaar voor de keuze tussen leven met een structuur die je tegenwerkt, of een migratie betalen die maanden kost en functioneel niets oplevert.

Die twee risico’s zijn niet vergelijkbaar. Bij de ene verlies je niets dat je op een factuur kunt zetten, bij de andere een kwartaal. Als de kosten van een verkeerde gok zo scheef liggen, hoor je de kant te kiezen waar de gok goedkoop is.

Dat is geen voorkeur voor Symfony omdat het beter zou zijn. Het is een keuze voor de optie waarbij het minst misgaat als de aannames niet kloppen. En aannames over groei kloppen zelden.

Hetzelfde principe, één laag lager

Wat voor frameworks geldt, geldt voor de rest van de stack. Dat is geen toeval: het is hetzelfde principe.

Voordat we een afhankelijkheid aangaan, vragen we hoe moeilijk het is om er weer vanaf te komen. Niet omdat we verwachten weg te gaan, maar omdat het antwoord iets zegt over de machtsverhouding. Een dienst waar je binnen een week vanaf kunt is een leverancier. Een dienst waar je twee jaar aan vastzit is een afhankelijkheid.

Datzelfde geldt voor infrastructuur. Draait het op Linux, containers en infrastructure as code, dan kun je het verplaatsen. Zit het vastgeklonken aan de bijzondere features van één cloudleverancier, dan is verplaatsen een project. Beide zijn verdedigbare keuzes, maar alleen als je ze bewust maakt en de prijs kent.

En op elk niveau geldt: als je later niet weet wat het systeem heeft gedaan, kun je het ook niet veilig veranderen. Logging, monitoring, back-ups en een audit trail zijn geen luxe die je toevoegt als het af is. Ze zijn de reden dat je er over vijf jaar nog aan durft te werken.

Waar het op neerkomt

Software die lang meegaat, is zelden de software die het slimst is gebouwd. Het is de software die je nog durft aan te raken.

Dat vraagt drie dingen. Een structuur die dwingt tot dezelfde keuzes, ook als er iemand anders aan werkt. Een upgradepad dat een proces is en geen project. En een architectuur waarbij de kosten van een verkeerde aanname klein blijven.

Aan het begin van een project weet je het minst en beslis je het meest. Daar is maar één verstandige omgang mee: kies zo dat de vergissing die je gaat maken, betaalbaar is.


Hexxore adviseert en bouwt software die jaren mee moet. Twijfel je over een architectuurkeuze, of heb je een applicatie die uit zijn jasje is gegroeid? Vertel het ons.

Alle artikelen