Leer hoe je feature-specs uit code maakt door echt gedrag uit routes en componenten te halen en er een living spec en een gaps-lijst van te maken.

Mensen verschillen van mening over wat een app doet omdat ze verschillende versies ervan herinneren. Support herinnert zich het laatste boze ticket. Sales herinnert zich het demo-pad. Engineers herinneren zich wat de feature moest doen. Vraag drie mensen en je krijgt drie vol vertrouwen gegeven antwoorden, en geen van die antwoorden komt overeen met de huidige build.
Na verloop van tijd wordt de code de enige bron die actueel blijft. Docs driften, tickets worden gesloten en snelle fixes stapelen zich op. Een route krijgt een nieuwe validatieregel. Een UI-toggle verandert een standaard. Een handler begint andere fouten terug te geven. Niemand werkt de spec bij omdat het optioneel aanvoelt en elke wijziging te klein lijkt om te documenteren.
Dat veroorzaakt voorspelbare problemen. Teams leveren veranderingen die edge-cases breken waarvan ze het bestaan niet kenden. QA test het gelukkige pad en mist regels die in handlers verborgen zitten. Nieuwe teamleden kopiëren gedrag van de UI zonder de echte beperkingen te begrijpen. Stakeholders debatteren meningen in plaats van te wijzen naar afgesproken gedrag.
Een goed resultaat is geen perfect document. Het is gedeelde helderheid. Iedereen moet zonder te raden kunnen antwoorden: "Wat gebeurt er als ik X doe?" en "Wat garandeert het systeem?" Je krijgt minder verrassingen, kortere reviewcycli en minder "Wacht, dat doet het al"-momenten omdat het team naar dezelfde waarheid kijkt.
Als een spec met de code overeenkomt, wordt het veilig om wijzigingen te plannen. Je ziet wat stabiel is, wat toevallig is en wat ontbreekt voordat je iets uitrolt.
Een living spec is een korte, bewerkbare beschrijving van wat de app vandaag daadwerkelijk doet. Het is geen eenmalig document. Het verandert telkens wanneer het gedrag verandert, zodat het team het kan vertrouwen.
Als mensen het over feature-specs uit code hebben (bijvoorbeeld met Claude Code), is het doel eenvoudig: lees het echte gedrag uit routes, handlers en schermen en schrijf het vervolgens op in begrijpelijke taal.
Een nuttige living spec focust op wat gebruikers zien en wat het systeem belooft. Het zou het volgende moeten behandelen:
Wat het niet zou moeten behandelen is hoe de code georganiseerd is. Als je begint met bestandsnamen en refactor-plannen, dwaal je af naar implementatiedetails. Vermijd:
Een gaps-lijst is apart. Het is een korte lijst van mismatches en onbekenden die je vindt tijdens het schrijven van de spec.
Voorbeeld: één route weigert bestanden groter dan 10MB, maar de UI zegt 25MB. Dat is een gap totdat het team beslist welke regel echt is en of ze de code of de spec bijwerken.
Begin klein. Als je probeert de hele app te documenteren, eindig je met een stapel notities die niemand vertrouwt. Kies één deel dat gebruikers in één zin kunnen beschrijven, zoals "een collega uitnodigen", "afrekenen" of "wachtwoord resetten". Goede scopes zijn een enkel featuregebied, één module of één gebruikersreis van ingang tot resultaat.
Kies je instappunt op basis van waar de waarheid leeft:
Voordat je de code leest, verzamel een paar inputs zodat mismatches snel opvallen: bestaande API-docs, oude productnotities, supporttickets en de "bekende pijnpunten" waar mensen over klagen. Deze overrulen de code niet, maar helpen je ontbrekende toestanden zoals fouten, edge-cases en permissies sneller te zien.
Houd het spec-formaat saai en consistent. Teams stemmen sneller af wanneer elke spec op dezelfde manier leest.
Gebruik deze structuur herhaaldelijk en je feature-specs blijven leesbaar, vergelijkbaar en makkelijk te updaten.
Begin bij server-instappunten. Routes en handlers tonen concreet "wat de app doet": wie het kan aanroepen, wat ze moeten sturen, wat ze terugkrijgen en wat er in het systeem verandert.
Maak een lijst van de routes binnen scope en koppel elke route aan een gebruikersintentie. Schrijf niet "POST /api/orders." Schrijf "Plaats een bestelling" of "Bewaar als concept." Als je de intentie niet in gewone woorden kunt benoemen, is dat al een spec-gap.
Lees elke handler en neem inputs en validatieregels op als gebruikersgerichte vereisten. Inclusief verplichte velden, toegestane formaten en regels die echte fouten veroorzaken. Bijvoorbeeld: "E-mail moet geldig zijn", "Aantal moet minimaal 1 zijn", "Startdatum mag niet in het verleden liggen."
Schrijf auth- en rolchecks op dezelfde manier. In plaats van "middleware: requireAdmin," documenteer: "Alleen admins kunnen elke bestelling annuleren. Gewone gebruikers kunnen alleen hun eigen bestelling binnen 10 minuten annuleren." Als de code ownership, featureflags of tenant-grenzen controleert, neem die ook op.
Noteer daarna outputs en uitkomsten. Wat geeft succes terug (een aangemaakte ID, een bijgewerkt object)? Hoe zien veelvoorkomende fouten eruit (401 niet ingelogd, 403 niet toegestaan, 404 niet gevonden, 409 conflict, 422 validatiefout)?
Tenslotte: noteer side effects omdat die deel uitmaken van het gedrag: aangemaakte of bijgewerkte records, verzonden e-mails of notificaties, gepubliceerde events, in de wachtrij geplaatste background jobs en alles dat andere flows triggert. Deze details voorkomen verrassingen wanneer teams later op de spec vertrouwen.
Routes vertellen wat de app kan doen. Components vertellen wat gebruikers daadwerkelijk ervaren. Beschouw de UI als deel van het contract: wat verschijnt, wat wordt geblokkeerd en wat gebeurt er als iets misgaat.
Begin met het vinden van de ingangsschermen voor de feature. Zoek de page-component, layout-wrapper en een paar "decision"-componenten die fetching, permissies en navigatie sturen. Daar leeft meestal het echte gedrag.
Terwijl je components leest, noteer regels die gebruikers kunnen voelen: wanneer acties uitgeschakeld zijn, verplichte stappen, conditionele velden, loading-states en hoe fouten verschijnen (inline veldfouten versus toast, auto-retry, "probeer opnieuw"-knoppen). Noteer ook state- en caching-gedrag zoals eerst verouderde data tonen, optimistische updates of "laatst opgeslagen"-tijden.
Let op verborgen flows die stilletjes veranderen wat gebruikers zien. Zoek naar featureflags, experiment-buckets en alleen-admin-toegangen. Noteer ook stille redirects, zoals het sturen van uitgelogde gebruikers naar inloggen of gebruikers zonder toegang naar een upgrade-scherm.
Een concreet voorbeeld: op een "E-mail wijzigen"-scherm documenteer je dat Opslaan uitgeschakeld blijft totdat de e-mail geldig is, dat er een spinner verschijnt tijdens het verzoek, dat succes een bevestigingsbanner toont en dat backend-validatiefouten onder het invoerveld worden weergegeven. Als de code een vlag toont zoals newEmailFlow, noteer dan beide varianten en wat er anders is.
Schrijf elke UI-flow als korte stappen (wat de gebruiker doet, wat de UI terugdoet) en plaats condities en fouten direct bij de stap waarop ze van toepassing zijn. Dat houdt de spec leesbaar en maakt gaps makkelijker te vinden.
Ruwe notities uit routes en components zijn nuttig, maar lastig om over te discussiëren. Herschrijf wat je observeerde in een spec die een PM, designer, QA en engineer allen kunnen lezen en goedkeuren.
Een praktisch patroon is één user story per route of scherm. Houd het klein en specifiek. Bijvoorbeeld: "Als ingelogde gebruiker kan ik mijn wachtwoord opnieuw instellen zodat ik weer toegang krijg." Als de code verschillend gedrag toont per rol (admin vs gebruiker), splits dit dan in aparte stories in plaats van het in voetnoten te verbergen.
Schrijf daarna acceptatiecriteria die echte codepaden spiegelen, niet het ideale product. Als de handler 401 teruggeeft wanneer de token ontbreekt, is dat een criterium. Als de UI indienen uitschakelt totdat een veld geldig is, is dat ook een criterium.
Neem dataregels op in eenvoudige taal, vooral die regels die verrassingen veroorzaken: limieten, ordening, uniciteit, verplichte velden. "Gebruikersnamen moeten uniek zijn (gecontroleerd bij opslaan)" is duidelijker dan "unieke index."
Edge-cases maken vaak het verschil tussen een mooi document en een nuttig document. Benoem lege staten, null-waarden, retries, timeouts en wat gebruikers zien wanneer een API-call faalt.
Wanneer je onbekenden tegenkomt, markeer ze in plaats van te raden:
Die markeringen worden snelle teamvragen in plaats van stille aannames.
Een gaps-lijst is geen tweede Jira. Het is een korte, op bewijs gebaseerde registratie van waar code en bedoeld gedrag niet overeenkomen, of waar niemand duidelijk kan uitleggen wat "correct" is. Goed gedaan wordt het een hulpmiddel voor overeenstemming, niet voor planningsoorlog.
Wees streng over wat als een gap telt:
Wanneer je een gap vastlegt, voeg dan drie delen toe zodat het bewijsgericht blijft:
Bewijs zorgt ervoor dat de lijst geen meningenlijst wordt. Bijvoorbeeld: "POST /checkout/apply-coupon accepteert verlopen coupons, maar CouponBanner.tsx blokkeert ze in de UI. Impact: omzetverlies en gebruikersverwarring. Type: bug of ontbrekende beslissing (bevestig de gewenste regel)."
Houd het kort. Zet een harde limiet, zoals 10 items voor de eerste ronde. Als je 40 issues vindt, groepeer ze in patronen (validatie-inconsistenties, permissiechecks, lege staten) en houd alleen de belangrijkste voorbeelden.
Vermijd data en planning in de gaps-lijst. Als je eigenaarschap nodig hebt, hou het lichtgewicht: noteer wie de beslissing moet nemen (product) of wie het gedrag kan verifiëren (engineering), en verplaats echte planning naar je backlog.
Kies een klein, veelgebruikt deel: checkout met promotiecodes en verzendopties. Het doel is niet om het hele product te herschrijven, maar om vast te leggen wat de app vandaag doet.
Begin bij backend-routes. Vaak verschijnen regels daar eerst. Je vindt mogelijk routes zoals POST /checkout/apply-promo, GET /checkout/shipping-options, en POST /checkout/confirm.
Schrijf vanuit die handlers gedrag in gewone woorden:
Controleer daarna UI-components. Een PromoCodeInput toont bijvoorbeeld totals pas na een succesvolle response en fouten worden inline onder het invoerveld weergegeven. Een ShippingOptions-component kan bij eerste laden automatisch de goedkoopste optie selecteren en een volledige prijsopbouw verversen wanneer de gebruiker deze wijzigt.
Nu heb je een leesbare spec en een kleine gaps-lijst. Bijvoorbeeld: foutmeldingen verschillen tussen de promo-route en de UI ("Invalid code" versus "Not eligible"), en niemand kan de duidelijke afrondingsregel voor belasting aanwijzen (per regel versus totaal van de bestelling).
Bij planning stemt het team eerst af op de realiteit en beslist daarna wat te veranderen. In plaats van meningen te bespreken, bekijk je gedocumenteerde gedragingen, kies je één inconsistentie om te verhelpen en laat je de rest als "huidig gedrag" staan totdat het de moeite waard is om het te herzien.
Een spec helpt alleen als het team het vertrouwt. Doe een korte doorlezing met één engineer en één productpersoon. Houd het strak: 20–30 minuten gericht op wat gebruikers kunnen doen en wat het systeem doet als reactie.
Tijdens de doorlezing verander je beweringen in ja/nee-vragen. "Wanneer een gebruiker deze route aanroept, geven we altijd 403 terug zonder sessie?" "Is deze lege staat opzettelijk?" Dit scheidt bedoeld gedrag van toevallig gedrag dat in de loop der tijd is ingeslopen.
Stem woordgebruik af voordat je iets aanpast. Gebruik woorden die gebruikers in de UI zien (knoppen, paginatitels, foutmeldingen). Voeg interne namen alleen toe wanneer ze engineers helpen de code te vinden (routenamen, componentnamen). Dit voorkomt mismatches zoals product dat "Workspace" zegt terwijl de spec "Org" noemt.
Om het actueel te houden, maak eigenaarschap en cadence expliciet:
Als je een tool zoals Koder.ai gebruikt, kunnen snapshots en rollback je helpen om "voor" en "na" gedrag te vergelijken bij het bijwerken van een spec, vooral na een grote refactor.
De snelste manier om vertrouwen in een spec te verliezen is het documenteren van het product dat je wilt, niet het product dat je hebt. Houd een harde regel: elke bewering moet worden ondersteund door iets waar je naar kunt wijzen in de code of op een echt scherm.
Een andere valkuil is het kopiëren van de code-structuur naar het document. Een spec die leest als "Controller -> Service -> Repository" is geen spec, het is een map van mappen. Schrijf in gebruikersgerichte termen: wat triggert de actie, wat ziet de gebruiker, wat wordt opgeslagen en hoe zien fouten eruit.
Permissies en rollen worden vaak genegeerd tot het einde, en dan loopt alles vast. Voeg toegangsregels vroeg toe, zelfs als ze rommelig zijn. Geef aan welke rollen kunnen bekijken, aanmaken, bewerken, verwijderen, exporteren of goedkeuren, en waar de regel wordt afgedwongen (alleen UI, alleen API of beide).
Sla non-happy-paths niet over. Echt gedrag zit verstopt in retries, gedeeltelijke fouten en tijdsgebonden regels zoals vervallen tokens, cooldowns, geplande taken of "slechts één keer per dag" limieten. Behandel deze als eersteklas gedrag.
Een snelle manier om gaps zichtbaar te maken is controleren op:
Zorg er tenslotte voor dat je gaps-lijst vooruitgaat. Elke gap moet gelabeld worden als: "unknown, needs decision," "bug, fix," of "missing feature, plan." Als niets gelabeld wordt, stagneert de lijst en stopt de spec met leven.
Doe een korte controle op duidelijkheid, dekking en uitvoerbaarheid. Iemand die het niet geschreven heeft moet begrijpen wat de feature vandaag doet en wat nog onduidelijk is.
Lees de spec als een nieuwe collega op dag één. Als diegene de feature in een minuut kan samenvatten, zit je dichtbij. Als diegene blijft vragen "waar begint dit?" of "wat is het happy path?" verscherp dan de introductie.
Controleer:
Elke gap moet specifiek en toetsbaar zijn. In plaats van "Foutafhandeling onduidelijk," schrijf: "Als de betaalprovider 402 teruggeeft, toont de UI een generieke toast; bevestig gewenste melding en retry-gedrag." Voeg één volgende actie toe (vraag product, voeg een test toe, inspecteer logs) en noteer wie het moet beantwoorden.
Kies één featuregebied en timebox het tot 60 minuten. Kies iets kleins maar reëels (inloggen, afrekenen, zoeken, een admin-scherm). Schrijf één zin scope: wat inbegrepen is en wat uitgesloten is.
Doorloop de workflow één keer end-to-end: scan belangrijke routes/handlers, traceer de hoofd-UI-flow en noteer observeerbaar gedrag (inputs, outputs, validatie, foutstaten). Als je vastloopt, log de vraag als een gap en ga door.
Wanneer je klaar bent, deel de spec op een plek waar het team kan reageren, en stel één regel: elke gedragswijziging die shipped wordt moet de spec bijwerken in hetzelfde leveringsvenster, ook al is het slechts vijf regels.
Houd gaps apart van de backlog. Groepeer ze in "onbekend gedrag," "inconsistente gedrag" en "ontbrekende tests," en review ze kort wekelijks om te besluiten wat nu belangrijk is.
Als opstellen en itereren traag aanvoelt, kan een chat-gebaseerde builder zoals Koder.ai je helpen snel een eerste versie te krijgen. Beschrijf de feature, plak belangrijke snippets of routenamen, verfijn de bewoording in gesprek en exporteer de bron wanneer je die nodig hebt. Het punt is snelheid en gedeelde helderheid, niet een zwaarder proces.
Begin met één kleine, voor de gebruiker zichtbare slice (bijvoorbeeld “wachtwoord opnieuw instellen” of “nodig een collega uit”). Lees de routes/handlers om regels en uitkomsten vast te leggen, en lees daarna de UI-flow om vast te leggen wat gebruikers daadwerkelijk zien (gedeactiveerde knoppen, fouten, redirects). Schrijf het op met een consistent sjabloon en noteer onbekende zaken als een aparte gaps-lijst.
Stel als uitgangspunt: documenteer het huidige gedrag van de code als bron van waarheid.
Als gedrag toevallig of inconsistent lijkt, los het niet op in de spec — markeer het als een gap met bewijs (waar je het zag en wat er gebeurt), en laat het team beslissen of de code of de spec aangepast moet worden.
Houd het saai en herhaalbaar. Een praktisch sjabloon is:
Dit houdt specs leesbaar en maakt mismatches makkelijker zichtbaar.
Schrijf regels als gebruikersgerichte vereisten, niet als code-aantekeningen.
Voorbeelden:
Leg vast wat een fout triggert en wat de gebruiker ziet wanneer dat gebeurt.
Focus op wat observeerbaar is:
Side effects zijn belangrijk omdat ze andere features en support/opsverwachtingen beïnvloeden.
Als de UI iets blokkeert wat de API toestaat (of andersom), noteer het als een gap totdat er een beslissing is.
Leg vast:
Stem daarna één regel af en werk zowel code als spec bij zodat ze overeenkomen.
Houd de gaps-lijst klein en op bewijs gebaseerd. Elk item zou moeten bevatten:
Vermijd dat het een tweede backlog wordt.
Documenteer ze expliciet in plaats van ze te verbergen.
Includeer:
Dit zijn vaak plekken waar verrassingen en bugs vandaan komen.
Houd het kort: een read-through van 20–30 minuten met één engineer en één productpersoon.
Zet beweringen om in ja/nee-vragen (bijvoorbeeld: “Returnen we altijd 403 wanneer niet toegestaan?”). Stem af op woordgebruik van de UI (labels en meldingen) zodat iedereen hetzelfde bedoelt.
Zet de spec dicht bij de code en maak updates onderdeel van releasen.
Praktische defaults:
Het doel is kleine, frequente aanpassingen — geen grote herzieningen.