Er is een manier om software-budget te verbranden die zelden bij naam genoemd wordt, omdat ze vermomd gaat als professionaliteit: overengineering. Meer complexiteit, meer flexibiliteit en meer architectuur bouwen dan het probleem feitelijk vraagt.
Het is precies de soort fout die een opdrachtgever zonder veel software-ervaring zelf moeilijk kan herkennen, omdat ze klinkt als zorgvuldig nadenken over de toekomst. "We willen het schaalbaar maken." "We willen het toekomstbestendig houden." "We willen geen vendor lock-in." Stuk voor stuk redelijke zinnen, en stuk voor stuk uitstekende dekmantels voor systemen die drie keer zo duur worden als nodig.
We schreven eerder over waarom B2B-software-projecten zo vaak vastlopen, met tien oorzaken van onduidelijke requirements tot het taaie laatste 20%. Overengineering komt daar terug bij punt 7 (verkeerde contractvorm), waar het rechtstreeks uit voortvloeit. Maar het is breder dan dat ene punt: het is geen losse oorzaak, maar een patroon dat zich doorheen meerdere oorzaken weeft. Scope creep, big-bang opleveringen, het laatste 20% dat nooit afraakt. Het verbergt zich liever in de offerte zelf dan dat het zich als duidelijke fout aandient.
In dit artikel: de patronen die we het vaakst zien, waarom ze gebeuren, en hoe we het bij Tandem Studio proberen op te zetten zodat we onze klanten er niet in meeslepen.
De werkelijke kost van overengineering
Het probleem zit hem niet alleen in de bouwprijs. Een eenvoudig systeem dat over één tabel gaat, kost je niet alleen meer om te bouwen wanneer je het opdeelt in vijf services met een eigen API en database. Het kost je ook meer bij elke wijziging, elke nieuwe ontwikkelaar die je inwerkt, elke bug-fix die door drie lagen moet, en elke developer-handover die later moeilijker wordt omdat de complexiteit zelf een entry barrier is geworden.
De factuur die je vandaag tekent, betaal je jarenlang elke maand opnieuw.
En er zit een onaangename asymmetrie in: te eenvoudige code die later moet worden uitgebreid voelt als een fout van de bouwer ("ze hebben er niet over nagedacht"), terwijl te complexe code die nooit benut wordt zelden expliciet wordt afgeschreven. Die kost zit verstopt in elke onderhoudsfactuur achteraf, niet in één duidelijk rapport.
De 8 patronen die we het vaakst zien
1. Hyperscalers als default. "We zetten het op AWS" rolt makkelijker uit de mond dan "we zetten het op een Scaleway-server in Parijs". Maar voor de overgrote meerderheid van KMO-applicaties (en zelfs voor een hoop SaaS-startups) is een eenvoudige Europese VM met Postgres, je applicatie en Traefik erop ruim voldoende. Verschil in factuur: 20 à 100 euro per maand versus honderden tot duizenden. Verschil in complexiteit: één machine die elke ontwikkelaar begrijpt versus een ecosysteem dat een gespecialiseerd profiel vereist. In onze vergelijking van AWS, GCP, Scaleway, OVH en Hetzner leggen we uit waarom we standaard voor Scaleway kiezen.
2. Microservices voor een team van vijf. Microservices zijn een organisatie-oplossing, niet een technische. Ze helpen wanneer meerdere teams onafhankelijk willen kunnen deployen op verschillende delen van een systeem. Voor een KMO of startup met één team is splitsing in zes services bijna altijd verlies: meer netwerkproblemen, meer deploys, meer plekken waar data inconsistent kan worden, meer operationele complexiteit. Een monoliet die je later eventueel opdeelt wanneer je écht groeit, is bijna altijd het juiste startpunt.
3. Kubernetes als cv-statement. Kubernetes is krachtig en behoorlijk complex. De vraag is niet "is het cool?" maar "hebben we het nodig?". Voor een applicatie die op één of twee servers prima draait, is docker compose met een Traefik-config alles wat je nodig hebt. Wat je opzet met die eenvoudige tools kan elke ontwikkelaar in een uur uitleggen. Wat je opzet met een Kubernetes-cluster, vraagt een specialist om aan te passen.
4. Configuratie voor waarden die nooit veranderen. Een admin-paneel waarin de BTW-percentages, e-mailsjablonen, workflowstappen en businessregels van je product instelbaar zijn, terwijl in de praktijk niemand die instellingen ooit aanpast. Elke configuratie-optie is code die geschreven, getest, gedocumenteerd en onderhouden moet worden. Een vaste waarde in code die een ontwikkelaar in vijf minuten kan aanpassen, is vaak goedkoper dan een fancy admin-scherm dat één keer per drie jaar wordt geopend.
5. "Platforms" in plaats van applicaties. Sommige offertes beloven geen applicatie maar een "platform": een generieke basis waarop jouw concrete probleem later "wordt gezet". Dat klinkt als meerwaarde en is in 90% van de gevallen overengineering verpakt als ambitie. Je betaalt dubbel: eerst voor de generieke laag, dan voor de concrete invulling. En je krijgt een product dat naar geen enkele use case écht goed past, omdat het naar alle moest passen.
6. Schalen voor gebruikers die je niet hebt. "Maar wat als we naar één miljoen gebruikers gaan?" is een vraag die een architectuur makkelijk drie keer zo duur maakt. Het eerlijke antwoord: als je dat aantal bereikt, dan heb je geld, klanten en kennis om iets beters te bouwen, gericht op wat dán werkelijk knelt. Vandaag bouwen voor een toekomst die zich misschien nooit voordoet, is bijna gegarandeerd ontwerpen tegen de verkeerde bottleneck.
7. Abstractielagen voor één implementatie. Een interface met één implementatie erachter "voor het geval er ooit een tweede komt". Een repository-laag bovenop een query builder die zelf al een abstractielaag is. Een factory die altijd hetzelfde object teruggeeft. Elke abstractie die niet minstens twee echte gebruikers heeft, is speculatieve complexiteit. Erger: vroege abstracties zijn bijna altijd verkeerd, want toen je ze ontwierp wist je nog niet waar het systeem heen zou groeien, waardoor ze later precies in de weg zitten en moeilijk te verwijderen zijn omdat de hele codebase erom heen gebouwd is.
8. Eigen authenticatie en gebruikersbeheer bouwen. Login, wachtwoorden, multi-factor, password reset, sessiebeheer, role-based access, OAuth-flows. Dit lijkt een paar weken werk en blijkt steevast een half jaar. En het is een aanvalsoppervlak dat je niet zelf wil onderhouden. Vraag eerst: kunnen we niet gewoon Zitadel, Clerk, Keycloak of zelfs Google/Microsoft Sign-In gebruiken? Negen op de tien keer is het antwoord ja.
Waarom dit zo vaak gebeurt
Er zijn drie krachten die overengineering structureel aandrijven, en geen ervan is "ontwikkelaars zijn dom".
De leverancier verdient aan complexiteit, niet aan eenvoud. Een voorstel met meer technologie ziet er voor een leek indrukwekkender uit dan een voorstel dat zegt "we bouwen dit in acht weken op één server". Een fixed-price offerte van zes maanden is voor veel partijen aantrekkelijker dan een opdracht die in zes weken klaar kan zijn.
Ontwikkelaars hebben een professionele drijfveer om interessante technologie te gebruiken. Niet uit slechte wil, maar omdat hun cv en hun toekomstige werkgevers naar die ervaring kijken. Kubernetes, GraphQL en event-driven architectures staan beter op een profiel dan "Go en een degelijke monoliet op Postgres". Dat is een eerlijke realiteit waar je als opdrachtgever rekening mee mag houden.
Binnen je eigen organisatie kan vaak niemand de technische beweringen tegenspreken. Als de leverancier zegt dat je "een service mesh", "een eventbus" of "een datalake" nodig hebt, is er zelden iemand intern die kan beoordelen of dat klopt. De default is: vertrouwen op de expert. Maar de expert heeft niet altijd jouw belang als belangrijkste kompas.
Hoe we het bij Tandem proberen te vermijden
Onze aanpak is niet exotisch. Ze is doelbewust saai op de plekken waar saai werkt.
We werken in spikes. Door per fase een afgebakend budget en een concrete vraag af te spreken, is er gewoon geen ruimte voor "een laag die we misschien later nodig hebben". Wat we vandaag bouwen, lost op wat we vandaag weten. Dat is een natuurlijke rem op speculatieve complexiteit. Hier lees je waarom we steeds vaker met spikes werken in plaats van klassieke offertes.
Onze default-stack is saai. Go als applicatietaal, Postgres als database, Traefik als reverse proxy, één of twee applicatieservers op Scaleway, geen Kubernetes tenzij er écht een operationele reden is. We schalen op wanneer een concreet meetbaar probleem het vraagt, niet vanuit speculatie.
Code en data zijn van jou. We bouwen bewust op een manier die overdraagbaar is. Geen exotische technologie die enkel onze mensen kunnen onderhouden, geen vendor lock-in op een specifieke cloud, geen frameworks die je aan ons binden. Als je morgen wil weggaan, kan je weggaan met alles mee. Dat dwingt ons om geen complexiteit te bouwen waar wij de enige expert in zijn. Opnieuw een natuurlijke rem.
Vaste prijs per fase. Geen incentive om uren op te rekken. Hoe sneller en pragmatischer we iets goed kunnen oplossen, hoe beter het voor beide kanten werkt.
Eerst vragen of we het wel moeten bouwen. Soms is het juiste antwoord op een vraag een goeie Notion-template, een Airtable-setup of een Zapier-flow. We zeggen dat dan ook, zelfs als het minder werk voor ons is. Een mislukt project dat we niet aangenomen hebben, is voor iedereen beter dan een project dat we wel deden maar dat zonder waarde eindigde.
Event sourcing: onze uitzondering op "saai is beter"
Eerlijk blijven: ook wij hebben voorkeuren. De meest opvallende voor wie naar onze stack kijkt, is event sourcing als architectuurpatroon. Wat we daarbij willen aangeven: in onze context werkt event sourcing juist tégen overengineering, niet ervoor.
De reden zit in hoe het patroon zich verhoudt tot het werken in spikes. Bij event sourcing leg je elke bedrijfsgebeurtenis vast als een onveranderlijk feit, in plaats van enkel de huidige staat. Dat maakt het mogelijk om vandaag écht klein te beginnen en morgen iets bij te bouwen zonder dat eerdere keuzes in de weg gaan zitten:
- Een nieuw scherm of een nieuwe view op dezelfde data is een nieuwe projectie naast de oude. De bestaande blijft ongeroerd.
- Audit-functionaliteit of dashboards kan je later toevoegen omdat de volledige geschiedenis er al in zit, geen retroactieve migraties.
- Een async worker of externe integratie hangt zich op een eventstream zonder dat de core moet wijzigen.
- Een nieuwe feature voegt meestal een event of een projectie toe in plaats van bestaande tabellen te verbouwen.
Met andere woorden: het is een patroon dat iteratief uitbreiden actief ondersteunt in plaats van blokkeert. Voor ons is het de manier om in spikes te kunnen groeien zonder bij elke nieuwe vraag een verbouwing te moeten doen, en dus precies een rem op het type overengineering waar dit artikel over gaat: vooraf alles willen voorzien omdat aanpassen achteraf duur is. Doordat we er zelf een framework voor hebben gebouwd, blijft de instapkost voor onze klanten beperkt. In ons artikel over wanneer event sourcing loont voor een KMO leggen we de afweging in detail uit, inclusief de gevallen waar je het beter níét doet.
Tegelijk blijft het feit dat we deze voorkeur hebben een vorm van bias: niet elk project schreeuwt om event sourcing, en we moeten ons bewust blijven van het "we hebben deze hamer en deze spijker ziet er bruikbaar uit"-effect. Wat we wel kunnen beloven: je hoort van ons waarom we een keuze maken, wat ze kost als ze achteraf fout blijkt, en wat het alternatief was geweest. Als je daarna een andere richting wil, prima.
Tot slot
De beste software voor de meeste KMO's ziet er saai uit. Eenvoudige, beproefde technologie, weinig bewegende onderdelen, snel iets in productie bij echte gebruikers, en pas complexiteit toevoegen wanneer een concreet probleem in de praktijk daarom vraagt.
Een leverancier die enthousiast wordt van die aanpak, is bijna altijd een betere partner dan een leverancier die enthousiast wordt van de architectuur.
Twijfel je of een voorstel dat op je bureau ligt overengineered is? We kijken er graag eens samen naar. Dat is precies het soort gesprek dat we het liefst voeren. Plan een kennismaking.


