Spec-driven development: kies een trede en reken de prijs uit
Spec-driven development is geen ja-of-nee-vraag maar een keuze tussen drie ambitieniveaus met sterk uiteenlopende kosten. Op bestaande codebases loopt de derde trede - de spec bepaalt de implementatie - vast, omdat verifieerbare acceptatiecriteria in de praktijk zelden bestaan en elke correctieronde dan een menselijke beoordeling kost. Kies bewust een trede, reken de prijs door en stel een complexiteitsdrempel in.
Ik heb spec-driven development een periode in de praktijk toegepast op bestaande codebases, met Spec Kit en OpenSpec. Twee teams, verschillende domeinen, allebei brownfield (bestaande) projecten. Ik heb de specs zelf geschreven, de gegenereerde code zelf beoordeeld, en de uitkomst geëvalueerd.
Mijn conclusie: SDD is geen ja-of-nee-vraag maar een keuze tussen drie ambitieniveaus, die elk een andere prijs hebben. De meeste discussies erover lopen vast omdat niemand benoemt op welk niveau hij staat - en omdat vrijwel niemand uitrekent wat het kost.
Wat werkt
Specificaties zijn een uitstekende drager van veranderingen. Een spec schrijven vóórdat er code is, dwingt de onduidelijkheden naar boven op het moment dat ze nog gratis zijn. Dat is een echte winst, en hij is niet afhankelijk van AI.
Het effect is niet overal even groot. Bij kleine wijzigingen - in ons geval alles onder drie storypoints - is het overhead: de tijd die het schrijven en beoordelen van de spec kost, is groter dan de tijd die het aan verwarring bespaart. Daar werkt een lichte planfase beter. Waar het zich terugverdient, is bij complexe wijzigingen die meerdere componenten of meerdere partijen raken en waar een misverstand duur is.
Een tweede winst voegden we onderweg zelf toe: laat het gereedschap testscenario's genereren naast de spec. Dat maakt het verwachte gedrag concreet in plaats van beschrijvend, en het geeft ontwikkelaars en testers meteen iets bruikbaars. Achteraf gezien is dit de belangrijkste aanpassing die we hebben gedaan.
De kosten zitten niet waar je ze verwacht
Een spec schrijven ging snel. Dat is ook wat je verwacht: het gereedschap produceert binnen minuten iets leesbaars en compleet ogend.
De kosten zaten in de cyclus erna. Spec, genereren, resultaat beoordelen, spec bijstellen, opnieuw genereren. Die lus liep bij ons net zo lang door als het bouwen met AI zonder een spec zou hebben geduurd. Op die wijzigingen leverde de spec per saldo geen tijdwinst op.
Twee verklaringen, die elkaar versterken.
De eerste is de uitgangssituatie. Er was code en er waren vrijwel geen specs. De specificatie moest dus worden afgeleid uit een implementatie die alle beslissingen al bevatte die de spec probeerde te beschrijven. Daarmee begin je met een achterstand: een deel van het werk is reconstructie van iets dat nergens was opgeschreven.
De tweede verklaring is belangrijker, want ze verklaart de eerste. De acceptatiecriteria in onze specs waren beschrijvend, niet machinaal controleerbaar. Daardoor was er maar één manier om te weten of een gegenereerd resultaat klopte: een mens leest het. Elke ronde in die lus kostte dus een menselijke beoordeling, en die kosten stapelen niet - ze herhalen zich.
De prijs van zo'n convergentielus - bijstellen en opnieuw genereren tot het resultaat klopt - is het aantal rondes maal de kosten van één ronde. Zonder geautomatiseerde controle kost elke ronde een mens die het resultaat leest en beoordeelt, en daar loopt de rekening op.
Wij hebben die kosten aanzienlijk verlaagd met twee eigen toevoegingen: testscenario's naast de spec, en unittests tijdens het bouwen. Ze doen niet hetzelfde. De scenario's maken vooraf concreet wat het gedrag moet zijn en toetsen dus of de intentie uit de spec is overgenomen; de unittests bevestigen achteraf dat de code doet wat de code doet. Alleen de eerste maakt de lus korter.
Daarmee zit de kern van het probleem niet in het gereedschap maar in het ambitieniveau: wie wil dat de spec de uitkomst bepaalt, maar de criteria alleen beschrijft, betaalt het verschil per iteratie in de duurste valuta die er is. Dat onderscheid - beschrijven, verifieerbaar maken, bepalen - werk ik hieronder uit.
Het verklaart ook waarom de winst bij complexe wijzigingen wél zichtbaar was. Daar zit de opbrengst niet in convergentie maar in het vroeg boven tafel krijgen van onduidelijkheden. Die lus ga je dan helemaal niet in.
Drie tredes
Wat nu volgt is mijn eigen indeling, geen bestaand model. Ze komt voort uit wat ik in deze twee projecten heb gezien en is bedoeld als hulpmiddel om de discussie scherper te maken. De verwarring rond SDD komt naar mijn idee voort uit het feit dat er drie verschillende ambities onder dezelfde term schuilgaan. Ze zijn alle drie legitiem, maar ze kosten niet hetzelfde.
Trede 1 - intentie vastleggen. De spec beschrijft wat je wilt bereiken en waarom. Goedkoop, werkt, en levert direct waarde. De prijs is één document per wijziging plus de tijd om het te beoordelen.
Trede 2 - gedrag verifieerbaar vastleggen. De spec bevat acceptatiecriteria die machinaal te controleren zijn. Ook dit werkt, maar het kost echt geld: uitvoerbare criteria schrijven is werk, en ze onderhouden is meer werk. In wat ik ben tegengekomen wordt die prijs zelden betaald, terwijl de ambitie er wel ligt.
Trede 3 - de implementatie bepalen. De spec is volledig genoeg om de code eruit te laten voortkomen; de spec wordt het besturingsmechanisme en de code het resultaat. Hier loopt het naar mijn overtuiging vast, en niet om redenen die met AI te maken hebben. Een concreet symptoom: volledige dekking betekent dat elke wijziging meedoet, hoe klein ook - een complexiteitsdrempel is op deze trede dus geen pragmatische uitzondering, maar het bewijs dat de praktijk ergens lager op de ladder zit.
Mijn argument tegen de derde trede gaat over informatie-inhoud, niet over kwaliteit. Om gedrag betrouwbaar te reproduceren moet een specificatie elke beslissing bevatten waarvan dat gedrag afhangt. Op het moment dat ze dat doet, bevat ze dezelfde informatie als de code. De vraag is dan niet of specs code kunnen vervangen, maar waarom je dezelfde informatie zou opslaan in een notatie zonder compiler, zonder typesysteem en met een niet-deterministische vertaler.
De kennis en theorie die een team over een systeem heeft, is niet volledig in documenten te vangen. Je legt de beslissingen vast, maar zelden de afgewogen alternatieven en de aannames eronder. Bij een gegroeide codebase zijn dat vele jaren.
Twee nuances horen erbij. In gesloten domeinen met machinaal controleerbare criteria werkt regeneratie prima: clients uit een API-beschrijving, formulieren uit een schema. En bij een greenfield (nieuw) project speelt het probleem nauwelijks, want er is nog geen gedrag om te behouden. Mijn conclusie gaat over gegroeide bestaande systemen.
Wat de praktijk verder liet zien
Specs zijn niet deterministisch. Draai hetzelfde probleem twee keer door hetzelfde gereedschap en je krijgt twee verschillende oplossingen. Dat betekent dat specs beoordeeld moeten worden zoals code beoordeeld wordt, en het betekent iets fundamentelers: als de spec de uitkomst niet vastlegt, is hij geen besturingsmechanisme maar een invoer.
Specs hebben een houdbaarheidsdatum. Blijft een spec te lang liggen, dan loopt hij uit de pas met de werkelijkheid. Een archief van samengevoegde specs is daarmee een verzameling intenties op een moment in de tijd, geen beschrijving van het systeem. De feitelijke geschiedenis houd je beter apart bij.
Bij wijzigingen tijdens het bouwen pasten we eerst de code aan en daarna de documentatie. De zuivere volgorde - spec bijwerken, opnieuw beoordelen, dan implementeren - kost te veel doorlooptijd. Dit is de eerlijkste zin uit onze bevindingen, want het erkent dat code in de praktijk nog steeds de bron van waarheid is. Het is ook geen AI-probleem: het is dezelfde spanning tussen documentatie en oplevering die al decennia bestaat.
Daar hoort een structurele opmerking bij. Twee met de hand onderhouden bronnen van waarheid is geen keuze maar een defect. Óf specs worden uit code afgeleid, óf code uit specs. Allebei bijhouden levert gegarandeerde drift op, en de onduidelijkheid die daarover in teams ontstaat is een symptoom, geen communicatieprobleem.
De rekensom
Het werk begint eerder dan de spec. Voordat er überhaupt iets te specificeren viel, was een aparte taak nodig om de eisen helder te krijgen: de beschikbare informatie was verouderd, de scope was onduidelijk en de acceptatiecriteria waren dat ook. Pas daarna volgt de spec zelf, met het plan en de takenlijst die er allemaal bij horen - en die allemaal beoordeeld moeten worden - plus de convergentielus hierboven.
Wat je ervoor terugkrijgt: minder correctiecycli tijdens de implementatie en minder herwerk uit verkeerd begrepen eisen.
Of dat per saldo wint, hangt volledig af van de omvang van de wijziging. Onze enige empirische uitspraak is de drempel van drie storypoints. Dat is geen wet, maar het is meer dan de meeste organisaties hebben - daar wordt SDD ingevoerd zonder dat het break-evenpunt ook maar benoemd wordt, laat staan gemeten.
Er zit ook een verborgen kostenpost in het gereedschap. Speckit produceert aanzienlijk meer documentatie dan OpenSpec voor een vergelijkbaar resultaat. Reviewtijd schaalt mee met de hoeveelheid uitvoer, niet met de waarde ervan. Wie SDD invoert met het meest uitgebreide gereedschap, koopt daarmee ongemerkt een reviewlast in.
En dan het punt waar het werkelijk om draait. Als codeschrijven niet de beperkende stap is in je doorlooptijd, levert sneller genereren per definitie niets op. De knelpunten die wij tegenkwamen waren geen van alle technisch: Figma-ontwerpen die niet meeliepen met de werkelijkheid van het product, functionele vragen die pas tijdens het bouwen bovenkwamen, en onduidelijk eigenaarschap van bepaalde beslissingen. Dat is ook precies waar die eerste verhelderingstaak zijn tijd aan kwijt was. Een proces dat de generatiestap optimaliseert terwijl de beperking elders ligt, verplaatst de wachttijd in plaats van hem weg te nemen.
Je wilt het juiste maken met betere invoer. Dat is iets anders dan het snel maken.
Een claim om voorzichtig mee te zijn
In een van de werkwijzen die ik tegenkwam staat dat na de verfijningsstap de functionele eis volledig duidelijk is en de omvang van het werk aan alle componenten bekend. Dat is de klassieke belofte van vooraf ontwerpen, en niets in onze bevindingen onderbouwt hem. Onze ervaring wees de andere kant op: één klantverzoek bleek twee user stories te zijn, de code werkte maar de pull request was te groot om te beoordelen, en tijdens het bouwen kwamen wijzigingen boven die niemand had voorzien.
Dat is geen kritiek op het schrijven van specs. Het is een waarschuwing tegen het formuleren van zekerheid die je niet hebt.
Wat ik zou aanraden
- Kies bewust een trede en reken de prijs door. Een organisatie die de derde trede als doel formuleert maar de eerste betaalt, creëert een verwachting die nooit wordt ingelost. Daar sneuvelt het vertrouwen in de werkwijze.
- Stel een complexiteitsdrempel in. Onder een bepaalde omvang draai je het proces niet. Bepaal je eigen grens, maar bepaal er één. Die drempel is alleen te verenigen met trede 1 of 2 - op trede 3, waar de spec de code genereert, breekt elke uitzondering de trede zelf, want dat is dan code zonder afgeleide spec: een tweede bron van waarheid naast de eerste.
- Laat de controle uit de spec voortkomen, niet uit de code. Testscenario's naast de spec zijn de goedkoopste stap omhoog op de ladder en ze maken de convergentielus betaalbaar. Unittests tijdens het bouwen zijn nuttig maar doen iets anders: scenario's uit de acceptatiecriteria toetsen wat je vroeg, tests uit de code toetsen wat je kreeg.
- Beoordeel specs zoals je code beoordeelt. Niet-determinisme maakt dit geen optie maar een voorwaarde.
- Verfijn de invoer voordat hij het proces in gaat. Een klantverzoek rechtstreeks in het gereedschap stoppen levert een spec op voor wat de klant beschreef, niet voor wat hij nodig heeft.
- Beoordeel incrementeel tijdens het bouwen, niet pas aan het eind. Dat voorkomt zowel onbeoordeelbare pull requests als het opnieuw moeten doen van werk dat halverwege al de verkeerde kant op ging.
- Regel de feedbackloop vóór het gereedschap. Bepaal expliciet wat de bron van waarheid is en hoe doorgevoerde wijzigingen terugslaan in de specs.
- Meet eerst waar je doorlooptijd werkelijk blijft hangen. Als dat niet bij het schrijven van code is, begin daar dan niet.
Waar ik ongelijk zou kunnen hebben
Mijn waarnemingen komen uit twee teams, in één soort omgeving, over een beperkte periode. Dat is een smalle basis voor een algemene conclusie, en de indeling in drie tredes is mijn eigen model, geen gevestigd kader.
Het is ook denkbaar dat de kosten van uitvoerbare specificaties zo hard dalen dat de tweede trede vanzelf de norm wordt. In dat geval zegt mijn conclusie vooral iets over wat het nú kost, en niet over wat principieel mogelijk is.
Wat ik wel met vertrouwen zeg: de winst zit in betere invoer en een beter proces, niet in sneller genereren. Dat deel is geen voorspelling maar rekenwerk.