Spaghetticode (spaghetti code) — is een verwarde, chaotische programmastructuur waar logische blokken zonder enige orde door elkaar zijn geweven. Volgens gegevens van het TIOBE Index (2024)-onderzoek hebben projecten met een hoog niveau van spaghetticode 2,5 keer meer tijd nodig voor het implementeren van nieuwe functies. De term ontstond in het tijdperk van het vroege programmeren, toen de goto-operator sprongen tussen willekeurige programmapunten mogelijk maakte en onleesbare constructies creëerde.
Belangrijkste
Spaghetticode (spaghetti code) — is een metafoor voor code waarvan de structuur lijkt op een bord spaghetti: individuele draden (logische blokken) zijn verward, aan elkaar geplakt en onlosmakelijk met elkaar verbonden. In dergelijke code kunnen geen lagen, modules of componenten worden onderscheiden — alles is door elkaar gemengd in één grote massa.
In tegenstelling tot slechte code, die gewoon slordig kan zijn, is spaghetticode een fundamenteel architectuurprobleem. Zelfs perfect geformatteerde code met goede variabelennamen kan spaghetticode zijn als de architectuur chaotisch is. Het probleem ligt op het niveau van de programmastructuur, niet op de schrijfstijl.
Volgens IEEE-gegevens (2022) wordt ongeveer 35% van alle fouten in grote projecten veroorzaakt door de verwarde codestructuur, niet door logische fouten van de ontwikkelaar. De ontwikkelaar maakt geen fout omdat hij de taak verkeerd begreep, maar omdat hij de uitvoeringsstroom in spaghetticode niet kon volgen.
Als slechte code slechte code is op de schaal van één functie of bestand, dan is spaghetticode slechte architectuur op de schaal van de hele applicatie. Spaghetti kan bestaan uit afzonderlijke goed geschreven functies, maar hun interactie is chaotisch en onvoorspelbaar.
De term „spaghetti code“ verscheen in de jaren 1970 samen met de kritiek op de goto-operator. In vroege programmeertalen (BASIC, FORTRAN, COBOL) was goto de belangrijkste manier om de uitvoeringsstroom te beheren. Het programma bestond uit een reeks genummerde regels en goto maakte het mogelijk naar elk ervan te springen. Dit creëerde een „kluwen“ van sprongen die onmogelijk te ontwarren was.
In 1968 publiceerde Edsger Dijkstra de beroemde brief „Go To Statement Considered Harmful“, die het begin markeerde van het tijdperk van gestructureerd programmeren. Dijkstra bewees dat elk algoritme zonder goto kan worden geïmplementeerd met slechts drie constructies: sequentie, vertakking (if) en lus (while). Dit werd de basis van modern programmeren.
Gestructureerd programmeren elimineerde het probleem niet volledig. Spaghetticode ging naar een nieuw niveau — in plaats van fysieke goto begonnen ontwikkelaars logische „goto“ te creëren: globale variabelen, callback hell in JavaScript, complexe aanroepketens en impliciete afhankelijkheden tussen componenten. Het probleem bleef, alleen de vorm veranderde.
Callback hell in JavaScript, diep geneste Promises, async/await zonder foutafhandeling, gebeurtenissen waarvan niet duidelijk is wie en wanneer ze activeert — dit zijn allemaal moderne varianten van spaghetticode. Het antipatroon leeft en bloeit, alleen gebruikt het nu geen goto-operator meer.
Gebrek aan lagen — het eerste en belangrijkste teken. In spaghetticode zijn bedrijfslogica, databasewerk, HTML-sjabloon en netwerkcommunicatie door elkaar gemengd in één bestand of zelfs in één methode. Het wijzigen van een databasequery kan de UI-weergave verstoren omdat de code van deze lagen niet is gescheiden.
Globale variabelen en singletons — het tweede duidelijke teken. Wanneer de applicatiestatus in globale objecten wordt opgeslagen, wordt de uitvoeringsstroom onvoorspelbaar. Elke functie kan de globale status wijzigen en traceren waar en wanneer dit gebeurde is praktisch onmogelijk.
God-classes en god-functies — het derde teken. Een klasse van 2000+ regels die verantwoordelijk is voor bedrijfslogica, weergave en gegevensverwerking — dit is typische spaghetticode. Een functie die 10 parameters accepteert en 5 verschillende dingen doet — ook.
| Kenmerk | Beschrijving | Voorbeeld |
|---|---|---|
| Lagen mengen | SQL-query’s in UI-code | Controller met directe database-schrijftoegang |
| Globale variabelen | Status overal toegankelijk | static SessionManager in elke klasse |
| God-classes | Één klasse doet alles | OrderManager van 3000 regels |
| Lange methoden | Functies zonder opsplitsing | Methode van 200 regels met 5 verantwoordelijkheden |
| Callback hell | Oneindig geneste callbacks | 6 niveaus van nesting in JavaScript |
Als je geen unittest voor een functie kunt schrijven zonder 15 mock-objecten te maken — dit is spaghetticode. Als het testen van één module het opstarten van de hele applicatie-infrastructuur vereist — dit is spaghetticode. Niet-testbaarheid — een objectieve indicator van verwarde architectuur.
Gebrek aan architectuurontwerp — de meest voorkomende oorzaak. Wanneer een team zonder plan code begint te schrijven en de architectuur „al doende“ kiest, verandert het resultaat onvermijdelijk in spaghetti. Elke nieuwe functie wordt toegevoegd waar „het nu handig is“, niet waar het logisch thuishoort.
Evolutionaire ontwikkeling — de tweede oorzaak. Een project begint als een klein script, groeit dan met functies, wordt dan een applicatie en vervolgens een monoliet. De architectuur wordt hierbij niet herzien. Wat werkte voor 100 regels code wordt een ramp voor 100.000 regels.
Schending van SOLID-principes — de derde oorzaak. Vooral het principe van enkele verantwoordelijkheid (S) en inversie van afhankelijkheden (D). Wanneer een klasse voor alles verantwoordelijk is, afhankelijkheden star zijn en modules stevig gekoppeld — ontstaat spaghetticode.
Deadlines en hotfix-cultuur — katalysatoren van spaghetticode. Wanneer „het was gisteren nodig“, voegen ontwikkelaars code in op de eerste de beste plek, zonder aan architectuur te denken. Tien van zulke hotfixes — en de applicatiearchitectuur is vernietigd.
Het belangrijkste gevolg — verlies van controle over de codebase. Ontwikkelaars stoppen met begrijpen hoe de applicatie als geheel werkt. Een wijziging op één plek breekt een andere, schijnbaar niet-gerelateerde plek. Elke patch creëert twee nieuwe bugs. Het team raakt in een staat van „angst voor veranderingen“ .
De productiviteit van het team neemt exponentieel af. Microsoft Research (2023) toonde aan dat de tijd voor het toevoegen van een nieuwe functie in spaghetticode kwadratisch groeit ten opzichte van de codebase-grootte. Voor schone architectuur is deze groei lineair. Het verschil wordt kritiek bij 50.000+ regels code.
Beveiliging — nog een slachtoffer. In spaghetticode is het gemakkelijk om een niet-afgevangen uitzondering, onjuiste invoervalidatie of gegevenslek over het hoofd te zien. Beveiligingsaudit in een project met verwarde architectuur is praktisch onmogelijk — het vinden van alle plaatsen waar gebruikersinvoer wordt gebruikt is onrealistisch.
Verloop in projecten met spaghetticode is bovengemiddeld. Ervaren ontwikkelaars vertrekken omdat ze niet met „spaghetti“ willen werken. Nieuwe medewerkers kunnen de code niet begrijpen en vertrekken in de eerste maanden. Het project verliest expertise, wat de codekwaliteit nog verder verslechtert — een vicieuze cirkel.
Ten eerste — begin met het scheiden van lagen. Verdeel de code in drie niveaus: presentation (UI, controllers), business logic (services, use cases) en data access (repositories, DAO). Zelfs gedeeltelijke scheiding verbetert onmiddellijk de structuur en maakt de code testbaar.
Ten tweede — implementeer dependency injection. Vervang directe aanmaak van afhankelijkheden door overdracht via constructor of parameters. Dit verbreekt starre verbindingen tussen componenten en maakt het mogelijk elke module geïsoleerd te testen.
Ten derde — extraheer god-classes en god-functies. Verdeel ze in kleine klassen en methoden met één verantwoordelijkheid. Gebruik het Facade-patroon om complexe subsystemen te vereenvoudigen. Onthoud: een klasse van 20 regels is begrijpelijker dan een klasse van 2000 regels.
// spaghetti — alles in één methode
function handleRequest(req, res) {
const db = new Database("mysql://...");
const user = db.query("SELECT * FROM users WHERE id =" + req.params.id);
let html = "";
html += ""
+ user.name + "";
html += "Balance: "
+ user.balance + "";
html += "";
res.send(html);
}
// schone architectuur — gescheiden lagen
class UserController {
constructor(userService) {
this.userService = userService;
}
async getUser(req, res) {
const user = await this.userService.findById(req.params.id);
res.json(new UserResponse(user));
}
}
class UserService {
constructor(userRepository) {
this.userRepository = userRepository;
}
async findById(id) {
return await this.userRepository.findById(id);
}
}
Probeer niet de hele codebase in één keer te herschrijven — dit is gegarandeerd falen. Kies één module, schrijf er tests voor (characterization tests) die het huidige gedrag vastleggen, en refactor pas daarna. Stap voor stap, module na module, ontwar je de spaghetti.
Architectuur planning — de basis van preventie. Voor aanvang van de ontwikkeling, keur de architectuurstijl goed: MVC, MVVM, Clean Architecture, VIPER of een andere. Schrijf een ADR (Architecture Decision Record) met de onderbouwing van de keuze. Eis naleving van de architectuur bij code review.
Het principe van inversie van afhankelijkheden (DIP) — een krachtig instrument in de strijd tegen spaghetticode. Modules van hoog niveau mogen niet afhankelijk zijn van modules van laag niveau. Beide moeten afhankelijk zijn van abstracties. Dependency Injection — de praktische implementatie van dit principe.
Testen — de beste preventie. Als je tests schrijft vóór de code (TDD), ontwerp je onvermijdelijk los gekoppelde componenten. Testbare code — is goed gestructureerde code. Niet-testbare code — is bijna altijd spaghetticode.
SonarQube — volgt cyclomatische complexiteit, overervingsdiepte, methodgrootte. JDepend (Java) — meet afhankelijkheden tussen pakketten. PhpMetrics — geeft de onderhoudbaarheidsindex voor PHP-projecten. Volg metrieken in CI/CD — voorkom het ontstaan van spaghetti, bestrijd het niet achteraf.
Veelgestelde vragen
Ja, stapsgewijze refactoring heeft de voorkeur. Gebruik de Strangler Fig-methode — vervang oude componenten geleidelijk door nieuwe zonder de werking van de applicatie te stoppen. Begin met het scheiden van de gegevenslaag of bedrijfslogica. Dek oude code af met tests vóór wijzigingen om functionaliteit niet te verliezen.
Spaghetticode — is het chaotisch dooreenvlechten van alle applicatielagen. Lasagnacode — een strikte gelaagde architectuur, maar elke laag is zo geïsoleerd dat gegevensoverdracht ertussen in bureaucratie verandert. Beide antipatronen zijn schadelijk, maar spaghetticode is gevaarlijker — het maakt code onvoorspelbaar.
Kijk naar afhankelijkheden: als een module modules uit alle lagen van de applicatie importeert — is dat verdacht. Let op de grootte van methoden — meer dan 30 regels is meestal slecht. Controleer of de functie werk met UI, bedrijfslogica en gegevens mengt. Zo ja — dan is het spaghetticode.
Clean Architecture van Robert Martin en Hexagonal Architecture (Ports & Adapters) — de twee beste benaderingen. Beide garanderen laagscheiding, onafhankelijkheid van bedrijfslogica van frameworks en testbaarheid. Voor mobiele ontwikkeling — MVVM met het Repository-patroon.
Gedeeltelijk. Metrieken zoals cyclomatische complexiteit (McCabe), modulekoppeling (Coupling) en overervingsdiepte (DIT) wijzen op potentiële spaghetticode. SonarQube, CodeClimate en PhpMetrics berekenen deze metrieken automatisch. Volledige diagnose vereist echter menselijke analyse van de architectuur.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook