Cyclomatische complexiteit: wat bouwen of refactoren

Samenvatting

Cyclomatische complexiteit is een metriek uit 1976 die onafhankelijke uitvoeringspaden door een functie telt. Scores boven 10 vereisen tests; boven 20 refactor je voor je iets nieuws toevoegt; boven 50 herschrijf je. Als beslissingskaart gebruikt geeft het je een prioriteitswachtrij: de hoog-CC-functies die je volgende feature raakt zijn je echte sprintplan. Dit artikel laat zien hoe je de scan uitvoert, de resultaten leest, en complexiteitsdata omzet in een shipping-beslissing.

Cyclomatische complexiteit drempelwaarden en beslissingsboom voor devs

Cyclomatische complexiteit: wat bouwen of refactoren

Cyclomatische complexiteit (CC) is de metriek die elke functie in je codebase een risicoscore geeft. Functies boven CC 10 hebben tests nodig voor je ze aanraakt. Boven CC 20 refactor je eerst voor je iets nieuws toevoegt. Boven CC 50 herschrijf je. Dat is de beslissingsboom. Dit artikel legt uit hoe je vandaag nog een scan uitvoert op je project en de output omzet in een sprintplan.

Je opent de codebase om 22:00. Je weet welke feature je wilt shippen. Twintig minuten later debug je iets wat er niets mee te maken heeft. De functie die je bewerkt is 300 regels lang, handelt zes verschillende gevallen af, en niemand -- inclusief jijzelf -- weet meer waarom het zo groot is geworden.

Dit is geen planningsprobleem. Het is een meetprobleem. Je had geen tool die vroeg waarschuwde. Cyclomatische complexiteit had dat kunnen zijn, en het had je die debug-sessie bespaard.

Wat cyclomatische complexiteit precies meet

Thomas McCabe publiceerde de metriek in 1976. De formule is M = E - N + 2P, waarbij E de kanten zijn, N de knopen, en P de verbonden componenten in de controlestroom-graaf van een functie. In de praktijk: begin bij 1, voeg 1 toe voor elke if, else if, for, while, case, catch, &&, of ||. Dat is de score.

Een functie zonder vertakking heeft CC = 1. Een functie die tien condities controleert, over een lijst itereert, en drie soorten fouten afhandelt, zit rond CC 15-20. Een functie die door zes devs is opgebouwd met patches over twee jaar kan CC 60 bereiken.

De metriek meet niet alles. Ze meet geen nestdiepte, naamkwaliteit of abstractieduidelijkheid. Een functie kan lage CC hebben en toch moeilijk leesbaar zijn. Maar een functie met CC boven 25 is bijna altijd moeilijk leesbaar. De correlatie in die richting is consistent genoeg om als prioriteitsfilter te dienen.

De drempelwaarden die je volgende stap bepalen

De meeste stijlgidsen en statische analysetools komen overeen in vergelijkbare bereiken:

De originele aanbeveling van NIST was een plafond van 10 per functie. In de praktijk behandelen de meeste engineers 15 als een realistische zachte grens voor productiecode, en alles boven 20 als een blokkade voor nieuw werk dat die functie raakt.

De tabel is je beslissingsboom voor wat je als volgende bouwt. Als je backlog vol is en je niet kunt beslissen of je een feature toevoegt of een bestaande module repareert, voer dan eerst een complexiteitsscan uit. De functies boven 20 zijn je verplichte stop voor alles nieuws ze aanraakt.

Grafiek van cyclomatische complexiteitsdrempels per risicobereik

Je eerste scan in 5 minuten uitvoeren

Elk groot taal-ecosysteem heeft een CLI-tool klaarstaan. Je hebt voor deze eerste stap geen SaaS-dashboard nodig.

Python (Radon):

pip install radon
radon cc -s -a ./src

JavaScript / TypeScript (ESLint):

{
  "rules": { "complexity": ["error", 10] }
}

Voer daarna eslint ./src --ext .ts,.js uit in je pipeline.

Go (gocyclo):

go install github.com/fzipp/gocyclo/cmd/gocyclo@latest
gocyclo -over 10 ./...

Java / Kotlin (PMD):

pmd check -d ./src -R rulesets/java/quickstart.xml | grep CyclomaticComplexity

De output geeft elke functie gesorteerd op CC-score. Wat je zoekt: de 10 functies met de hoogste score in je project. Sla die lijst op. Dat is je prioriteitswachtrij voor de volgende sprint.

Het rapport lezen als een beslissingskaart

Functies met hoge CC clusteren op voorspelbare plekken. In een aanbevelings-API van ongeveer 8.000 regels surfacete radon cc -s drie functies met CC boven 30. Die drie zaten allemaal in de data-normalisatielaag. Die laag was incrementeel gepatcht over zes maanden, elke keer voor een ander randgeval van een databron. Niemand had ooit naar de cumulatieve schade gekeken.

Elke nieuwe feature die de normalisatielaag raakte, duurde 40% langer om te shippen. Er werd twee dagen besteed, voor de volgende sprint, aan het refactoren van die drie functies: CC-scores van 32, 28 en 31 naar 7, 5 en 8 gebracht. De volgende sprint was de snelste in vier maanden. Drie functies. Twee dagen. Vier maanden vertraging, verklaard.

Het patroon is consistent in codebases: ongeveer 10-15% van de functies bevatten 70-80% van de complexiteit in elk organisch gegroeid project. Die concentratie is je echte roadmap.

Voor side projects specifiek telt dit zwaarder dan in teaminstellingen. Je kunt een rommelige functie niet aan een collega doorgeven. De volledige cognitieve kosten van het herbeginnen bij CC 30-code vallen op jou, meestal om 22:00, zes maanden nadat je het hebt geschreven.

Het tweede ding dat complexiteitsrapporten goed doen: ze geven je een concreet antwoord als je verlamd bent tussen "een feature toevoegen" en "eerst opruimen". De scan verwijdert de ambiguïteit. Als de feature hoog-CC-code raakt, ruim je eerst op. Als niet, ship je. Dat is een beslissing, geen discussie.

Dat klinkt simpel, maar het elimineert weken van verborgen technische schuld die je pas ontdekt als je er middenin zit. De complexiteitsscan maakt het onzichtbare zichtbaar voor je begint. En dat levert je iets op wat waardevoller is dan schone code: voorspelbare velocity.

Wanneer hoge complexiteit de juiste keuze is

Niet elke hoog-CC-functie is een probleem. Een betaalstatus-machine die 12 transactiestatussen afhandelt, moet echt 12 statussen afhandelen. Een parser die 15 grammaticaregels verwerkt, heeft 15 echte gevallen. Die platslaan naar 15 helperfuncties met CC 1 elk reduceert de complexiteit niet -- het verspreidt het over bestanden die nu moeilijker samen te navigeren zijn.

De nuttige vraag is niet "is dit CC te hoog?" maar "is dit CC hoger dan het domein vereist?" Een routeringsfunctie die 15 URL-patronen afhandelt met CC = 18 is waarschijnlijk passend. Een gebruikersprofiel-update-functie met CC = 18 die groeide door geaccumuleerde patches voor randgevallen die niemand had gepland, is een ander probleem.

Houd CC onder 15 op je kritieke paden. Laat het hoger lopen in echt complexe domeinen, en documenteer de reden inline. De functies die je later verrassen zijn altijd de functies zonder rechtvaardiging voor hun complexiteit dan geaccumuleerde fixes.

Een nuttige gewoonte: voeg een commentaarregel toe wanneer je bewust een functie boven CC 15 laat. Schrijf wat het domein vereist en waarom splitsen hier geen voordeel geeft. Zes maanden later bedank je jezelf.

Verspreiding van complexiteitsscores over functies in een typische organisch gegroeide codebase

Complexiteitschecks toevoegen aan je CI

De scan eenmalig handmatig uitvoeren is nuttig. Er een haak van maken in CI is wat het gedrag over tijd echt verandert. De praktische opstelling: stel een drempelwaarde in je linterconfiguratie in, laat de build mislukken als een nieuwe functie die overschrijdt, en sla het complexiteitsrapport op als CI-artefact bij elke run. De lijst bestaat bij elke build, zonder dat iemand eraan hoeft te denken het te genereren.

Voor GitHub Actions met een Python-project:

- name: Complexiteitscheck
  run: |
    pip install radon
    radon cc -n C -s ./src
    if [ $? -ne 0 ]; then exit 1; fi

Voor JavaScript met ESLint al in de pipeline, voeg "complexity": ["error", 12] toe aan je regels. Elke PR die een functie boven 12 pusht, mislukt automatisch.

Een praktische teamstandaard: CC <= 15 als harde CI-blokkade, CC 11-15 als waarschuwing die een inline commentaar vereist met de zakelijke reden. Die tweede laag dwingt het gesprek af zonder elke codereview een onderhandeling over drempelwaardeuitzonderingen te maken.

Drie tools voor continue tracking

De bovenstaande CLI-tools geven je een eenmalige snapshot. Voor continue zichtbaarheid op projectniveau steken drie tools eruit.

SonarQube is de meest complete optie voor projectbrede complexiteitsregistratie. De community-editie dekt cyclomatische complexiteit, cognitieve complexiteit en testdekking in één dashboard. Quality gates kunnen een merge blokkeren als nieuwe code de complexiteit boven een drempelwaarde pusht. Dit is de tool om als eerste naar te grijpen als een project meer dan twee bijdragers heeft en een echt reviewproces.

CodeScene koppelt complexiteitsscores aan commit-geschiedenis en brengt functies aan de oppervlakte die zowel complex zijn als vaak worden gewijzigd. Die intersecties zijn je echte technische schuld -- niet alleen moeilijk te begrijpen, maar actief tijdkosten bij elke sprint. Als je een argument moet maken voor een refactoring-sprint op de roadmap van een team, is de output van CodeScene het argument.

Code Climate Quality is de lichtere optie, geschikt voor solo-projecten en open-source repos. GitHub-integratie werkt out of the box, de standaarden zijn verstandig, en de gratis laag voor publieke repos is bruikbaar. Als je een side project beheert en complexiteitsdrift wilt bijhouden zonder infrastructuur op te zetten, begin hier. Je koppelt je repo, en elke PR krijgt automatisch een complexiteitsrapport. Geen deployment, geen onderhoud, geen maandelijkse factuur voor een team van een.

Wat de metriek je werkelijk dwingt te beslissen

Cyclomatische complexiteit zegt je niet of je code goed is. Het zegt je waar het risico is geconcentreerd. Dat is een nuttiger stuk informatie.

Voer de scan uit op je huidige project voor je volgende sprint. Kijk naar de top 10 functies op score. Vraag welke je volgende feature aanraakt. Als het antwoord "drie ervan, allemaal boven CC 20" is, is je sprintplan duidelijk: breng die drie functies onder CC 10, ship dan de feature. Het zal de helft van de tijd kosten die het anders zou hebben gekost.

Voor builders: als je op zoek bent naar je volgende side project en je werkt in echte codebases, is je complexiteitsrapport een spec. De meest complexe module die de velocity blokkeert -- een die complex is en voortdurend wordt aangeraakt -- is een tool die de moeite waard is om rond te bouwen. Een lichtgewicht complexiteitsdashboard dat CC-scores correleert met git blame-data en frequentie van gewijzigde bestanden, geprijsd voor een team van twee in plaats van een enterprise-contract, is een project dat nog niet volledig bestaat. De tooling-gap is echt.

Veelgestelde vragen

Wat is een goede cyclomatische complexiteitsscore?
Een CC tussen 1 en 10 wordt algemeen als acceptabel beschouwd. De originele aanbeveling van NIST was een plafond van 10 per functie. Scores tussen 11 en 20 vereisen review en testdekking voor aanpassing. Alles boven 20 is een sterke kandidaat voor refactoring voor er nieuw feature-werk die code aanraakt.
Hoe bereken je cyclomatische complexiteit?
De formule is M = E - N + 2P, waarbij E kanten zijn, N knopen, en P verbonden componenten in de controlestroom-graaf. In de praktijk: begin bij 1 en voeg 1 toe voor elke if, else if, for, while, case, catch, en logische operatoren && en ||. Elke grote statische analysetool doet dit automatisch.
Hoe beïnvloedt cyclomatische complexiteit side projects?
Bij solo builds compoundt hoge CC snel. Je hebt geen team om context mee te delen. Een functie geschreven bij CC 30 vier maanden geleden kost een dag herbeginnen voor je het veilig kunt aanpassen. Kritieke-pad-functies onder CC 15 houden is een van de meest impactvolle investeringen in toekomstige shipping velocity.
Is cyclomatische complexiteit hetzelfde als cognitieve complexiteit?
Nee. Cyclomatische complexiteit telt onafhankelijke paden door controlestroom, wat direct overeenkomt met het minimale aantal testgevallen dat nodig is. Cognitieve complexiteit, ontwikkeld door SonarSource, weegt nestdiepte en controlestroom-onderbrekingen -- het weerspiegelt beter de mentale inspanning van het lezen van code. Beide zijn nuttig; CC wordt universeler ondersteund in tooling.
Wanneer herschrijf je in plaats van te refactoren?
Wanneer een functie CC boven 50 heeft en geen betekenisvolle testdekking, vereist veilig refactoren tests die je niet kunt schrijven omdat de code te verstrengeld is om geïsoleerd te testen. Een herschrijving met tests vanaf nul is vaak sneller en veiliger. De meeste engineers plaatsen het praktische omslagpunt tussen CC 40 en 60.
Welke tools meten cyclomatische complexiteit?
Radon voor Python, de ESLint complexity-regel voor JavaScript en TypeScript, gocyclo voor Go, PMD voor Java, SonarQube voor meertalige enterprise-projecten, Code Climate Quality als lichtere meertalige optie, en CodeScene voor complexiteit gecorreleerd met git-geschiedenis. De meeste CI-platforms integreren met ten minste een van deze.
Kan cyclomatische complexiteit kunstmatig te laag zijn?
Ja. Elke vertakking uitpakken naar een eenregelige helper verlaagt de CC van de originele functie zonder de echte complexiteit te reduceren -- het verspreidt het over bestanden. Een functie met CC 1 die alle vertakking delegeert aan ondoorzichtige helpers is misleidend. Gebruik CC als een signaal naast een leesbaarheidsbeoordeling, niet als een getal om tegen elke prijs te minimaliseren.