Maak AI-gegenereerde code reviewbaar door mappen, naamgeving en invarianten te standaardiseren, zodat een menselijk team veilig kan overnemen en wijzigingen kan uitrollen.

AI-prototypes slagen vaak om één reden: ze brengen je snel naar “werkend”. Het probleem begint wanneer “werkend” moet worden “onderhoudbaar door een team”. Een prototype kan shortcuts verdragen omdat dezelfde persoon (of dezelfde chatthread) alle context heeft. Een team heeft die context niet.
AI-gegenereerde code kan ook lastiger aanvoelen om te reviewen dan door mensen geschreven code, omdat de intentie niet altijd zichtbaar is. Menselijke code laat meestal een spoor na: consistente patronen, herhaalde keuzes en een paar opmerkingen die uitleggen waarom iets bestaat. AI-output kan correct zijn, maar toch stijlen mixen, patronen tussen bestanden verschuiven en aannames verbergen op plekken waar reviewers ze niet verwachten.
Het doel is voorspelbaarheid: voorspelbare plekken, voorspelbare namen, voorspelbaar gedrag. Als een collega kan raden waar iets staat, hoe het heet en hoe het zich gedraagt, wordt reviewen een snelle controle in plaats van een detectiveverhaal.
Wat er meestal misgaat wanneer een prototype een teamproject wordt:
userId vs userid vs user_id), waardoor zoeken onbetrouwbaar wordt en bugs makkelijker gemist worden.Kleine inconsistenties vermenigvuldigen de onderhoudstijd omdat ze herhaalde beslissingen afdwingen. Als elk nieuw scherm een net iets andere maplocatie, componentnaam en data-fetchingstijl heeft, kunnen reviewers geen stabiel mentaal model opbouwen. Ze moeten de code elke keer opnieuw leren.
Een realistisch voorbeeld: een niet-technische oprichter gebruikt een vibe-codingtool om snel een eenvoudige CRM te maken. Het demo’t goed, maar wanneer een klein team het overneemt, vinden ze drie verschillende manieren om auth-state op te slaan, twee naamgevingsstijlen voor React-componenten en businessregels verspreid over UI-code en backend-handlers. Niks is “kapot”, maar elke wijziging voelt risicovol omdat niemand weet welke patronen de echte zijn.
Overdracht wordt makkelijker als je keuzes reduceert. Teams bewegen sneller als de codebase consequent vertelt wat de volgende stap is.
“Reviewbaar” betekent dat een nieuwe ontwikkelaar het repo kan openen, de juiste plek kan vinden om iets te veranderen, de wijziging kan uitvoeren en kan bevestigen dat verder niets kapot is gegaan. Dat is basaal, en veel AI-prototypes missen dat.
Om AI-gegenereerde code reviewbaar te maken, focus je minder op slimheid en meer op hoe veilig een mens het kan aanraken. Reviewbaarheid draait om het verlagen van het risico van verandering.
Wanneer een collega een pull request reviewt, proberen ze een paar vragen snel te beantwoorden:
Kleine diffs helpen, maar “klein” is niet alleen regel-aantal. Het betekent ook stabiele grenzen: een wijziging in één gebied zou geen aanpassing in ongerelateerde bestanden moeten vereisen.
Je hoeft geen perfectie te hebben. Je hebt conventies nodig, wat documentatie, een paar tests en vangrails die toekomstige drift voorkomen.
Een reviewer voelt zich veiliger wanneer ze snel kunnen zien:
Voorbeeld: je bouwde een React-frontend en een Go API. Het prototype werkt, maar de “create customer”-flow is verspreid over UI-code, API-handlers en databasecalls met licht verschillende veldnamen. Reviewbaar maken betekent die namen op elkaar afstemmen, de API-grens duidelijk houden en de regels opschrijven (bijvoorbeeld: “email moet uniek zijn” en “status mag alleen active of paused zijn”).
Streef niet naar het herschrijven van alles tot het eruitziet als een leerboekproject. Overdrachtsklare code is helder, consistent en veilig om aan te passen, ook al is het nog niet het mooiste resultaat.
Een team vergeeft imperfecte code. Waar ze moeite mee hebben, is niet weten waar iets leeft. Als je wilt dat AI-gegenereerde code reviewbaar is, maak het project makkelijk scanbaar: een klein aantal top-level mappen, consistente namen en één duidelijke plek voor configuratie.
Houd de top-level kaart stabiel naarmate de app groeit. Veel overdrachten falen omdat er voor elk experiment nieuwe mappen verschijnen. Scheid in plaats daarvan drie verantwoordelijkheden: app-compositie (screens, routes), kernbusinessregels en infrastructuur.
Hier is één praktisch patroon dat je kunt aanpassen (webapp-voorbeeld):
/
/app # routes/pages and UI composition
/core # domain logic: entities, rules, use-cases
/ui # reusable components, styles, design tokens
/infra # db, api clients, queues, auth adapters
/config # env schema, feature flags, app settings
/scripts # local tooling, seed data, one-off tasks
/docs # handoff notes, invariants, decisions
Als je eerste versie snel gegenereerd is, houd die scheiding zichtbaar. Zet vervangbare gegenereerde modules onder iets als /generated, en houd door mensen bewerkte modules onder /core of /app. Het doel is per ongeluk bewerken van code die je later opnieuw genereert te voorkomen.
Doe voorafgaand aan de overdracht een korte navigatietest met een collega (of je toekomstige zelf). Vraag waar de login-UI woont, waar autorisatieregels leven, waar database-toegang gedefinieerd is, waar API-base-URLs en featureflags staan en waar “speciale” scripts wonen.
Als een antwoord begint met “dat hangt ervan af” of “zoek ernaar”, pas dan de structuur aan totdat elk onderwerp één enkele, saaie thuis heeft. Dat saaie gevoel is wat onderhoud snel en veilig maakt.
Een naamgevingsconventie is een belofte: een reviewer moet kunnen raden wat iets is, waar het woont en hoe het gebruikt wordt voordat ze het bestand openen.
Begin met bestandsnamen en hou vast aan één stijl in het hele repo. Een eenvoudig standaard is: mappen in kebab-case, React-componenten in PascalCase en niet-component TypeScript-bestanden in camelCase. Doorbreek de regel alleen wanneer het ecosysteem het verwacht (bijvoorbeeld standaard Flutter-conventies of standaardbestanden zoals README).
Namen moeten intentie onthullen, niet implementatie:
BillingSummaryCard.tsx (wat het voorstelt)StripeCard.tsx (bindt een vendorkeuze in)RenderBilling.tsx (beschrijft hoe, niet waarom)Wees streng met vage bakjes. Bestanden utils, helpers of common worden snel rommelbakken, vooral wanneer code in korte periodes wordt gegenereerd. Als je gedeelde code nodig hebt, noem het naar scope en doel, zoals auth/tokenStorage.ts of billing/billingCalculations.ts.
Feature-mappen beschrijven de gebruikers-probleemruimte. Technische mappen beschrijven cross-cutting infrastructuur. Ze mixen verbergt grenzen.
Een praktische split is features zoals billing, onboarding, inventory, en technische gebieden zoals api, db, routing, design-system. Wanneer je meerdere clients hebt (web, server, mobile), maakt het behoud van dezelfde feature-namen over lagen heen veranderingen makkelijker te traceren.
Gebruik deze korte rubriek in code review:
Hernoem vroeg. Hernoemingen zijn goedkoop tijdens overdracht en duur nadat het team bovenop verwarring is gaan bouwen.
Een invariant is een regel waarop je app vertrouwt om correct te blijven, zelfs als features veranderen. AI-gegenereerde code werkt vaak omdat de generator een set regels aannam, maar die regels kunnen alleen in prompts of in iemands hoofd leven. Schrijf ze op zodat reviewers weten wat niet stilletjes mag verschuiven.
Goede invarianten zijn saai, specifiek en testbaar. Vermijd vage uitspraken zoals “valideer inputs.” Zeg precies wat is toegestaan, wie wat mag doen en wat er gebeurt als de regel wordt gebroken.
De meeste overdrachtsproblemen komen uit dezelfde gebieden:
Als je de zin in een unittest of API-test kunt veranderen, zit je op het juiste niveau.
Zet invarianten waar mensen er tijdens review natuurlijk naar kijken:
Vermijd het verbergen van invarianten in lange docs die niemand opent. Als het niet tijdens normale PR-review verschijnt, wordt het gemist.
Formuleer elke invariant met scope, regel en handhavingspunt. Voorbeeld: “Voor alle endpoints onder /api/projects/:id moet de requester lid van het project zijn; afgedwongen in auth-middleware en opnieuw gecontroleerd bij task-updates.”
Als een invariant verandert, maak het expliciet. Werk de docs bij, verwijs naar de code-locaties die veranderden en voeg of update een test die zou falen onder de oude regel. Anders behoudt het team vaak half de oude en half de nieuwe logica.
Als je een vibe-codingplatform gebruikt zoals Koder.ai, is een nuttige overdrachtsstap om het te vragen welke invarianten het aannam tijdens het genereren van de app. Zet dat om in een kleine set testbare regels die het team kan reviewen en bijhouden.
Een overdracht is niet hetzelfde als “het draait op mijn machine.” Het doel is het project makkelijk leesbaar, veilig aan te passen en voorspelbaar te maken voor iemand nieuw die het opent.
Begin met het bevriezen van de scope. Kies een datum en een korte lijst van wat stabiel moet blijven (kernschermen, sleutel-flows, integraties). Schrijf ook op wat expliciet buiten scope is zodat niemand functies blijft toevoegen terwijl jij probeert op te ruimen.
Doe daarna opruiming voordat je iets nieuws toevoegt. Hier begint reviewbaarheid te verschijnen: de codebase gedraagt zich als een product, niet als een demo.
Een praktische volgorde:
Houd het smoke-testplan klein maar reëel. Voor een React-app met een Go API en Postgres kan dat zijn: inloggen, een record aanmaken, verversen, bevestigen dat het persistent is en bevestigen dat een beperkte actie faalt.
Doe één reviewcyclus die zich richt op leesbaarheid, niet op features. Vraag een collega 30 minuten te besteden aan het beantwoorden van: “Kan ik dingen vinden?” “Komen namen overeen met gedrag?” “Zijn de invarianten duidelijk?” Los op wat hen vertraagt en stop dan.
Doe vlak voor de overdracht een “verse ogen”-test. Vraag iemand die het prototype niet bouwde het repo te openen en hardop te vertellen wat ze denken dat het doet. Als ze startpunten niet snel kunnen vinden, betaalt het team die kost bij elke wijziging.
Een eenvoudige regel: een nieuwe ontwikkelaar moet de hoofdingangspunten in minder dan twee minuten kunnen vinden. Dat betekent meestal een duidelijke README die één of twee plekken noemt om te starten (webapp-entry, API-entry, config) en dat die bestanden niet weggestopt zijn.
Controleer ook de reviewgrootte. Als sleutelmodules eindeloos scrollen vereisen, stoppen reviewers met het opmerken van problemen. Split lange bestanden zodat elk een enkele taak heeft en in één keer te begrijpen is.
Een korte overdrachtschecklist:
validateUser valideert, schrijft niet ook naar de database.Maya is een niet-technische oprichter. Ze bouwde een MVP door het product in chat te beschrijven: een eenvoudige CRM voor een klein dienstverlenend bedrijf. Het werkt: login, klanten, deals, notities en een basis admin-scherm. Na een paar weken huurt ze twee ontwikkelaars in om het van “werkt op mijn laptop” naar iets waar het bedrijf op kan vertrouwen te tillen.
Op dag één beginnen ze niet met herschrijven. Ze beginnen met de code reviewbaar maken. Hun eerste stap is de app in twee bakken te verdelen: core-modules (dingen waarop elke feature afhankelijk is) en features (schermen en workflows die gebruikers raken). Dat geeft ze een plek om beslissingen neer te leggen en een plek om wijzigingen te stoppen.
Ze stemmen een eenvoudige feature-map af: core (auth, database-toegang, permissies, logging, UI-componenten) en features (customers, deals, notes, admin).
Vervolgens passen ze de mappen aan om bij die map te passen. Voorheen lagen bestanden verspreid met gemixte naamgeving zoals CustomerPage.tsx, customer_view.tsx en custPageNew.tsx. Daarna heeft elke feature één thuis en is core-code duidelijk gescheiden. Reviews gaan sneller omdat pull requests meestal binnen één feature-folder blijven, en core-wijzigingen meteen opvallen.
Een kleine naamgevingsregel doet het meeste werk: “mappen zijn zelfstandige naamwoorden, componenten zijn PascalCase, functies zijn werkwoorden en we gebruiken geen afkortingen.” Dus custPageNew.tsx wordt CustomerDetailsPage.tsx en doStuff() wordt saveCustomerNote().
Ze schrijven één belangrijke regel op die altijd waar moet zijn en plaatsen die in een korte INVARIANTS.md binnen de feature-map.
Voorbeeldinvariant voor de CRM:
Alleen de eigenaar van de deal of een admin mag een deal bewerken. Iedereen kan hem bekijken, maar mag status, waarde of notities niet wijzigen.
Die zin stuurt backendchecks, databasequeries en frontend UI-states. Als iemand later “bulk edit” toevoegt, weten reviewers precies wat er niet stuk mag gaan.
Na een week is de code niet perfect, maar de overdracht is reëel:
AI kan je snel een werkend prototype geven. Het probleem is dat “werkend” vaak afhankelijk is van verborgen aannames. Wanneer een team er later aan werkt, breken kleine wijzigingen dingen op onverwachte plekken.
Een veelgemaakte fout is alles in één keer refactoren. Grote schoonmaakbeurten voelen bevredigend, maar maken het moeilijk te zien wat er veranderde en waarom. Stel eerst grenzen: beslis welke modules stabiel zijn, waar nieuwe code is toegestaan en welk gedrag niet mag veranderen. Verbeter daarna één gebied tegelijk.
Een ander veelvoorkomend probleem is gedupliceerde concepten met verschillende namen. AI creëert gemakkelijk zowel UserService als AccountManager voor dezelfde taak, of plan vs pricingTier voor één idee. Kies één term voor elk kernconcept en hernoem consistent in UI, API en database.
Verborgen regels zijn ook een grote bron van broosheid. Als de echte businesslogica in prompts of chatgeschiedenis zit, wordt het repo moeilijk te onderhouden. Zet de regels in de codebase als duidelijke comments, tests of een korte invarianten-doc.
Catch-all-mappen zoals shared, common of utils worden stilletjes rommelbakken. Als je gedeelde modules nodig hebt, definieer wat ze bezitten (inputs, outputs, verantwoordelijkheden) en houd ze scherp.
Businessregels mixen in UI-code is een andere val. Een snelle conditional in een React-component wordt de enige plek waar een prijsregel bestaat. Later is de mobiele app of backend het er niet mee eens. Houd businessregels in één laag (vaak backend of een domeinmodule) en laat de UI die aanroepen in plaats van opnieuw te implementeren.
Tot slot ontstaat broze code vaak door het overslaan van reviewnormen. Teams hebben kleine diffs, duidelijke commits en heldere intentie nodig. Zelfs als een generator de wijziging produceerde, behandel het als een normale PR: houd scope klein, leg uit wat er veranderde en maak het makkelijk te verifiëren.
Behandel overdracht als het begin van onderhoud, niet als het eindpunt. Het doel blijft simpel: een nieuwe persoon moet een kleine wijziging kunnen maken zonder verborgen regels te breken.
Zet teamvoorkeuren om in een paar vastgelegde defaults: één mappenkaart die iedereen volgt, één naamgevingsstijl en één template voor invarianten. Wanneer die regels vooraf worden afgesproken, stoppen reviewcommentaren met persoonlijke smaak en worden ze consistente checks.
Houd een “handoff README” bij die naar de paar plekken verwijst die ertoe doen: waar invarianten leven, hoe de app te draaien, hoe veilig een feature toe te voegen en wat je niet moet veranderen zonder overleg. Een nieuwe collega zou antwoorden binnen vijf minuten moeten vinden.
Als je workflow omkeerbaarheid ondersteunt, gebruik die. Bijvoorbeeld, Koder.ai ondersteunt snapshots en rollback, wat een eenvoudig vangnet kan zijn vóór refactors of dependency-upgrades. Als je klaar bent om eigenaarschap over te dragen, geeft het exporteren van de source code uit koder.ai het team een schoon startpunt voor normaal Git-werk.
Begin met de code voorspelbaar te maken. Stem mappen, naamgeving en grenzen op elkaar af zodat een collega kan raden waar dingen staan en hoe ze zich gedragen zonder het hele repo te doorzoeken.
Kies één patroon voor elk terugkerend werk (auth-state, data-fetching, validatie, foutafhandeling) en pas dat overal toe. Het doel is niet het ‘beste’, maar ‘consistent’, zodat reviewers de app niet steeds opnieuw hoeven te leren.
Een reviewbare codebase laat een nieuwe ontwikkelaar de juiste plek vinden om iets te wijzigen, een kleine aanpassing doen en die veilig verifiëren. Als wijzigingen vaak in ongerelateerde bestanden terechtkomen of er geraden moet worden welke regels gelden, is het nog niet reviewbaar.
Gebruik een klein, stabiel aantal top-level mappen en houd elke zorg in één duidelijke plek. Scheid app-compositie (routes/screens), kernbusinessregels en infrastructuur zodat navigatie seconden kost, niet detectivewerk.
Plaats code die je mogelijk opnieuw genereert onder een duidelijke map zoals /generated, en houd handmatig bewerkte code in stabiele gebieden zoals /core en /app. Zo voorkom je per ongeluk overschrijven en maak je eigenaarschap tijdens reviews duidelijk.
Kies één conventie en hanteer die overal: consistente schrijfwijze voor mappen en bestanden, consistente componentnamen en consistente veldnamen tussen UI, API en database. Consistentie maakt zoeken betrouwbaar en vermindert subtiele bugs door mismatchende namen.
Invarianten zijn regels die waar moeten blijven terwijl het product verandert, zoals permissies, unieke constraints en toegestane statusovergangen. Ze opschrijven maakt verborgen aannames zichtbaar zodat reviewers ze kunnen beschermen.
Plaats ze waar mensen ze echt zien: een kort hoofdstuk in de README plus korte aantekeningen direct naast de code die de regel afdwingt. Als de regel niet tijdens een normale PR-review verschijnt, wordt hij vergeten.
Bevries eerst de scope door een klein aantal kerngebruikersreizen te kiezen die moeten werken en wat expliciet buiten scope valt. Normaliseer daarna mappen en namen, verwijder dode code, voeg een minimale smoke-test checklist toe en doe één reviewronde die zich alleen op leesbaarheid richt.
Vermijd grote refactors die alles tegelijk aanraken, catch-all-mappen zoals utils, en businessregels begraven in UI-conditionals of chatgeschiedenis. Let ook op gedupliceerde concepten met verschillende namen en driftende validatie/foutafhandeling over endpoints en schermen.