Back to all posts

1.3 miljoen regels, 630 PR's per week: zo houden we een vibe-coded codebase beheersbaar

1.3 miljoen regels, 630 PR's per week: zo houden we een vibe-coded codebase beheersbaar

In juli 2026 schreef Robert C. Martin dat hij de code die zijn coding agents produceren niet meer leest.

Die uitspraak kreeg veel aandacht. De rest van zijn bericht is belangrijker om te begrijpen hoe hij werkt. Martin omringt de agents met unittests, Gherkin-acceptatietests, QA-procedures, mutatietests, coverage checks en kwaliteitsmetingen. Code verdient zijn vertrouwen door dat systeem te doorlopen.

Hij had een paar maanden eerder een vergelijkbaar punt gemaakt. In plaats van de implementatie te inspecteren, kijkt hij naar testdekking, afhankelijkheidsstructuur, cyclomatische complexiteit, modulegrootte en mutatietestresultaten. Toen mensen dit interpreteerden als het volledig opgeven van review, verduidelijkte hij: "Ik review genoeg — alleen niet de code."

Zijn opmerkingen beschrijven een praktisch probleem in AI-gedreven softwareontwikkeling. Coding agents kunnen sneller wijzigingen produceren dan mensen ze kunnen lezen. Zodra het volume AI-gegenereerde code de reviewcapaciteit van het team overstijgt, kan regel-voor-regel inspectie niet langer de enige bron van vertrouwen zijn.

Wij kampen bij vm0 de afgelopen acht maanden met hetzelfde probleem. Het grootste deel van de implementatie in onze repository wordt door agents geschreven — vibe coding, in de populaire afkorting — terwijl zes engineers verantwoordelijk blijven voor wat die code in productie doet.

De korte versie

vm0 is een vibe-coded codebase: agents schrijven het grootste deel van de implementatie, en zes engineers zijn eigenaar van het gedrag in productie. Acht maanden verder bevat de repository 1,329,170 regels en werden er 630 pull requests in één week gemerged. Vijf mechanismen dragen de kwaliteitslast die regel-voor-regel review niet meer alleen kan dragen.

  • Gelijke omgevingen. Engineers en agents werken in dezelfde dev-container, en elke pull request kan een eigen databasebranch krijgen. Een commando dat voor een agent werkt, werkt ook voor een persoon.
  • Uitvoerbare beperkingen. Strikte TypeScript, ongeveer 135 getypeerde API-contractmodules, Oxlint, ESLint en architectuurregels, met CI die geen waarschuwingen accepteert. Een regel die mechanisch te controleren is, wordt niet aan review overgelaten.
  • Boundary-georiënteerde tests. Een testing trophy in plaats van een testpiramide: integratietests via publieke modulegrenzen, echte PostgreSQL en echte migraties, mocks alleen bij externe systemen. Testcode is ongeveer 36 procent van de repository.
  • Volledige feedbackloops. Agents starten de applicatie en database, voeren migraties uit en bedienen een echte browser, en rapporteren dan de uitgevoerde commando's, het geteste pad en screenshots. De mediane pull request merge na ongeveer 53 minuten.
  • Continue opschoning. Knip verwijdert deterministisch dode code, dagelijkse workflows ruimen AI-residu op dat geen enkele linter kan benoemen, en een patroon dat vaak genoeg terugkomt wordt gepromoveerd tot lintregel of type constraint.

Wat vibe coding en agentic coding echt betekenen

Vibe coding is een LLM om code vragen, uitvoeren wat het oplevert, om aanpassingen vragen en geen aandacht besteden aan de gegenereerde code zelf. Martin Fowler's definitie is bewust smal en benadrukt de zin "vergeet dat de code überhaupt bestaat." Het past bij prototypes, wegwerpsoftware en kleine tools met beperkte gevolgen.

Agentic coding, door Fowler agentic programming genoemd, is wat teams doen als ze verwachten het resultaat te onderhouden. Een agent leest de repository, bewerkt bestanden, voert tests uit en werkt zelfstandig gedurende langere tijd, terwijl mensen eigenaar blijven van structuur en gedrag en het bewijs van een run beoordelen: testresultaten, kwaliteitssignalen, previews en productiegedrag.

Het verschil zit niet in hoeveel code een model schrijft, maar waar menselijke aandacht naartoe gaat.

Vibe codingAgentic coding
Autonomie agentPrompt, uitvoeren, opnieuw promptenLeest de repository, bewerkt, test, herhaalt
Wie leest de codeNiemandMensen lezen wat risico draagt
Wat wordt geverifieerdOf de output er goed uitzietTypes, contracten, tests, previews, productiesignalen
Beste toepassingPrototypes en wegwerptoolsSoftware met onderhoudshorizon
Typische mislukkingCode die niemand begrijptVerificatie te zwak voor het volume

Agents die een repository bewerken zijn één tak van een bredere verschuiving; we bekeken de niet-developer kant apart.

We noemen vm0 vaak een vibe-coded project omdat die term gangbaar is geworden voor software die vooral door AI is geschreven. In Fowlers termen ligt het dichter bij agentic coding. Onze engineers typen minder van de implementatie dan vroeger, maar blijven eigenaar van architectuur, onderhoudskosten en productiegedrag.

Naarmate codegeneratie sneller werd, verschoof meer van die verantwoordelijkheid naar de ontwikkelomgeving, het type-systeem, de test-suite en geautomatiseerde onderhoudsworkflows.

Acht maanden groei in een vibe-coded codebase

De vm0-repository werd aangemaakt in november 2025. Ten tijde van ons laatste wekelijkse engineering scale report was deze ongeveer acht en een halve maand oud.

De repository bevatte:

MetriekAantal
Niet-lege logische regels1,329,170
Productieregels850,913
Testregels478,257
Bronbestanden5,549
Testbestanden1,327
Packages44
Commits op de main branch14,005

Deze aantallen komen uit ons wekelijkse engineering scale report voor de week van 20–26, 2026, direct gemeten uit de monorepo. Testcode was ongeveer 36 procent van de gemeten codebase. Dat is een aandeel van het codevolume, geen testdekking, maar het geeft een indruk van hoeveel implementatie er rond verificatie is opgebouwd.

Over drie recente volle weken maakten zes engineers respectievelijk 541, 640 en 631 commits. In de week van 20–26 juli registreerde GitHub 630 gemergede pull requests. Zes engineers schreven er 556, terwijl release-automatisering de overige 74 verzorgde.

De mediane tijd van het openen tot het mergen van een pull request was ongeveer 53 minuten. Het 90e percentiel lag rond de 7.4 uur.

Bij dit tempo blijft menselijke review nuttig, maar kan het niet het hele kwaliteitssysteem dragen. We hebben onafhankelijke signalen nodig op verschillende punten in de levenscyclus van een wijziging.

Onze huidige aanpak kent vijf terugkerende thema's: gelijke omgevingen, uitvoerbare beperkingen, boundary-georiënteerde tests, volledige feedbackloops en continue opschoning.

Ontwikkelomgeving gelijktrekken met dev-containers

Veel moeilijk te reproduceren fouten komen voort uit status die nooit in een pull request verschijnt.

Een ontwikkelaar kan een ongedocumenteerde omgevingsvariabele hebben, een globaal geïnstalleerde tool, een oud configuratiebestand of een database die al maanden draait. Mensen wennen aan deze details. Agents zien ze meestal niet.

We zijn geleidelijk gestopt met het host-systeem als standaard ontwikkelomgeving te behandelen. Engineers en agents werken binnen een dev-container. Het ontwikkelimage bevat de projecttoolchain, PostgreSQL, pgvector, Chromium en browserautomatiseringstools.

CI draait versiebeheerbare toolchain-images, gebouwd uit dezelfde multi-stage Dockerfile-familie. De ontwikkel- en CI-images hebben verschillende doelen, en productie heeft zijn eigen deploymentvorm. Het nuttige kenmerk is convergentie: toolversies, afhankelijkheden en runtime-aanname zijn expliciet en versieerbaar.

Dit geeft ons een heldere verwachting. Commando's die een agent uitvoert, moeten reproduceerbaar zijn door een engineer in dezelfde ontwikkelcontainer. Checks die tijdens ontwikkeling slagen, moeten onder een verwante toolchain in CI draaien.

We passen een vergelijkbaar idee toe op data. Elke pull-request preview kan een eigen databasebranch krijgen en echte migraties en seed-stappen uitvoeren. Een agent kan data aanmaken, wijzigen en destructieve tests herhalen zonder de bestaande database van een ontwikkelaar te lenen of status van een andere pull request te erven.

Omgevingswijzigingen gaan via de repository. Tool-upgrades, browserversies en database-extensies worden op dezelfde manier gereviewd en doorgevoerd als applicatiecode.

Engineeringstandaarden uitvoerbaar maken in types en lintregels

Geschreven standaarden helpen mensen ontwerpbeslissingen te begrijpen. Ze garanderen minder dat elke wijziging die beslissingen volgt, zeker tijdens lange agentsessies.

We leggen stabiele regels vast in het type-systeem, linters en CI wanneer de regel betrouwbaar uit te drukken is.

vm0 gebruikt strikte TypeScript-instellingen, waaronder strict en noUncheckedIndexedAccess. Sommige packages activeren extra checks voor ongebruikte waarden en impliciete returns.

De API gebruikt een schema-first getypeerde REST-contractlaag, gebouwd met tRPC's type-mechanisme en Zod-schema's. Drizzle koppelt database-toegang aan TypeScript-types. Op het moment van deze review bevatte de repository ongeveer 135 contractmodules.

Deze keuzes vangen geen verkeerde productbeslissingen. Ze signaleren wel interface-drift vroeg. Als een veld of response verandert, falen gerelateerde aanroepen meestal tijdens statische analyse. De foutmelding is vaak specifiek genoeg voor een agent om in de volgende iteratie te gebruiken.

Linting dekt een tweede groep regels. Het platform draait Oxlint, type-bewuste checks en ESLint, samen met project-specifieke architectuurregels. CI accepteert geen waarschuwingen. Waarschuwingen die mogen blijven, worden achtergrondruis, en die is makkelijk te negeren door zowel mensen als agents.

Als een probleem zich herhaalt, overwegen we waar de check thuishoort:

  1. Kan het type-systeem het uitdrukken?
  2. Kan een linter of structurele tool het accuraat detecteren?
  3. Vereist het semantisch oordeel over de hele repository?

De eerste twee geven snel, deterministisch feedback op elke wijziging. De derde groep behandelen we met terugkerende workflows, later in dit artikel beschreven.

Foutgevoelige patronen in AI-gegenereerde code beperken

Sommige taalfeatures hebben legitiem gebruik, maar komen ook vaak voor in kwetsbare agent-gegenereerde code.

try/catch kan een foutgrens vervagen. Als een agent een fout tegenkomt, is het toevoegen van een catch-blok en fallback een makkelijke manier om het huidige pad te laten lopen, maar het originele probleem te verbergen. Het mixen van Promise .then() en .catch() met async/await verspreidt de control flow over verschillende stijlen.

React's useEffect veroorzaakt een vergelijkbaar probleem in state management. Het wordt vaak gebruikt om state te kopiëren, twee bronnen van waarheid te synchroniseren of een volgorde-afhankelijkheid te coderen die lastig te zien is vanuit het datamodel.

Het core webplatform beperkt deze patronen standaard. Een gerechtvaardigde uitzondering mag blijven, met een expliciete uitleg erbij.

De productiecode in het core webplatform bevat momenteel geen useEffect. We gebruiken ccstate en andere effectloze patronen om afhankelijkheden en side effects explicieter te modelleren. Een klein aantal productie-useEffect-aanroepen bestaat nog elders in de monorepo, vooral in gedeelde UI- en desktopcode, dus de scope van de claim is van belang.

Deze regels zijn ontstaan uit terugkerende fouten in deze repository. Als een patroon herhaaldelijk hetzelfde onderhoudsprobleem veroorzaakt, verschuiven we het van reviewadvies naar een uitvoerbare beperking.

Testen op modulegrenzen: een testing trophy, geen piramide

vm0 volgt geen conventionele testpiramide. Onze testgids beschrijft een testing trophy: statische analyse aan de basis, integratietests als dominante laag en een klein aantal end-to-end tests voor kritieke gebruikersflows.

Unittests zijn relatief zeldzaam. We gebruiken ze selectief voor beveiligingsgevoelige logica, algoritmes en statemachines. Het meeste bedrijfslogica wordt getest via de publieke grens van een module.

API-tests roepen de echte applicatie aan via de contracten. Testdata wordt waar mogelijk via productie-endpoints aangemaakt, en assertions gebeuren via publiek gedrag. Tests vermijden directe toegang tot interne services of het aanpassen van databasetabellen, omdat dat ze koppelt aan de huidige implementatie.

Interne infrastructuur blijft echt waar de kosten redelijk zijn:

  • PostgreSQL en pgvector
  • databasemigraties
  • het bestandssysteem
  • interne services
  • mocks bij externe-systeemgrenzen

Deze integratietests draaien langzamer dan sterk geïsoleerde unittests en vereisen een vollere omgeving. Ze verkleinen ook het gat tussen "de test slaagde" en "de applicatie kan deze operatie uitvoeren met een echte database."

Boundary-tests laten ruimte voor grote interne refactors. Een agent kan modules herstructureren, een service splitsen of de data-accesslaag wijzigen terwijl het publieke gedrag beschermd blijft.

Uncle Bob kiest een andere mix, met veel unittests, Gherkin en mutatietests. Het gedeelde principe is onafhankelijke verificatie. Het proces dat de code genereerde mag niet de enige bron zijn die beweert dat de code werkt.

Coding agents een volledige feedbackloop geven

Onze vroege coding-agent runs eindigden vaak met een bekend rapport: de code is aangepast en TypeScript slaagde; start de applicatie en controleer de pagina.

Dat laat de helft van de ontwikkelcyclus bij de engineer. Iemand moet nog steeds een database voorbereiden, de services draaien, een browser openen, data aanmaken, de fout observeren en deze terugkoppelen aan de agent.

Agents hebben nu genoeg van de ontwikkelomgeving om meer van dat werk zelf te doen. Ze kunnen de applicatie en database starten, migraties uitvoeren, testdata aanmaken, een pull-request preview bezoeken en echte browserinteracties uitvoeren.

Browserverificatie vangt een klasse problemen die typechecks en API-tests niet goed dekken: kapotte navigatie, laadstatussen die niet afronden, permissiefouten die alleen in een volledige flow optreden en visuele regressies.

Aan het einde van een run rapporteert de agent de uitgevoerde commando's, het geteste pad en wat hij observeerde. Bij UI-wijzigingen kan hij screenshots toevoegen. Een engineer kan dat bewijs inspecteren voordat hij beslist of hij de preview zelf opent.

Een screenshot is geen test en bewijst niet dat er geen andere fouten zijn. Het verlaagt wel de drempel om de context van de agent te reconstrueren. Voor een kleine interfacewijziging zijn reproduceerbare stappen en een eindscreenshot veel nuttiger dan een bericht dat de wijziging "zou moeten werken".

De kwaliteit van het werk van een agent hangt sterk af van de feedback die hij kan krijgen zonder op een persoon te wachten.

Branches kort houden met trunk-based development

Snelle codegeneratie kan een grote voorraad branches opleveren.

Langlevende featurebranches stapelen mergeconflicten, dubbel werk en verouderde context op. Naarmate pull requests groeien, wordt review moeilijker en trager. We gebruiken trunk-based development om branches kort te houden en continu te integreren rond main.

Onze main-branch regels vereisen:

  • pull requests voor wijzigingen
  • lineaire geschiedenis en squash-merges
  • een merge queue
  • Turbo-, Rust- en securitychecks
  • geen routinematige omzeiling van verplichte gates

Automatisering volgt hetzelfde pad. Een agent kan een pull request aanmaken, en sommige onderhoudstaken met laag risico kunnen automatisch mergen, maar de wijziging gaat alsnog door CI en de merge queue.

De grootte en levensduur van pull requests verklaren hoe 630 ervan in één week konden mergen. Een kleine wijziging heeft minder context, is makkelijker te verifiëren en botst minder snel met productwerk of een andere geautomatiseerde reparatie.

Knip als repository-garbage collection: dode code verwijderen

Codegeneratie voegt vanzelf bestanden en abstracties toe. Verwijderen vereist vaak een aparte prompt.

Na een refactor kunnen oude bestanden in de repository blijven staan. Het verwijderen van een feature kan exports, afhankelijkheden en entry points achterlaten. Deze artefacten breken zelden tests, maar maken de codebase op termijn moeilijker te doorgronden.

We gebruiken Knip om ongebruikte bestanden, exports, afhankelijkheden en entry points te vinden. TypeScript kan bevestigen dat de code geldig is; Knip vraagt of het nog deel uitmaakt van het systeem.

Het residu heeft een extra kost voor coding agents. De repository is een van hun belangrijkste contextbronnen. Een verouderde helper of verlaten implementatie kan voor de volgende agent als een goedgekeurd patroon lijken. Die context schoonhouden is het onaantrekkelijke deel van context engineering: wat een agent uit de repository leest, is net zo goed input als de prompt die hij kreeg.

Dode code verwijderen verbetert ook de input voor toekomstige runs. Knip handelt het deterministische deel van dat werk snel genoeg af om een vaste kwaliteitscheck te zijn.

Terugkerende AI-slop opschoningsworkflows

Knip en ESLint hebben duidelijke grenzen. Veel vormen van degradatie vereisen projectcontext en semantisch oordeel.

We gebruiken "AI slop" als praktische term voor dit residu: onnodige fallbacks, dubbele abstracties, tests die een publieke grens omzeilen of defensieve branches voor onmogelijke staten. Elk geval lijkt onschuldig. Gezamenlijk maken ze de repository moeilijker te begrijpen en geven ze toekomstige agents slechte voorbeelden.

Diverse terugkerende vm0-workflows scannen op deze patronen volgens schema.

Een dagelijkse AI-slop cleanup zoekt naar nieuw residu en selecteert een kleine set fixes met hoge zekerheid en laag risico. Andere workflows inspecteren API-tests die interne services benaderen, React- en ccstate-antipatterns en technische schuld die veilig kan worden aangepakt.

Elke workflow houdt de wijzigingen beperkt. Er wordt een pull request geopend, waarna de normale typechecks, lintregels, tests en merge queue gelden. Een pull request dat voor automatisch mergen is geconfigureerd, moet alsnog dezelfde gates passeren.

Agents op schema draaien is niet gratis, en we schreven apart over hoe die kosten te beperken. De workflows proberen niet in één keer alle technische schuld op te ruimen. Een kleine dagelijkse batch is makkelijker te verifiëren en minder verstorend dan een grote schoonmaak elke paar maanden.

Als een terugkerende workflow vaak genoeg hetzelfde patroon vindt, overwegen we de check naar ESLint, Knip of het type-systeem te verplaatsen. De semantische workflow is een plek om de regel te observeren en verfijnen voordat het een goedkopere deterministische check wordt.

Flaky test failures omzetten in geautomatiseerde reparaties

Een andere groep workflows begint bij GitHub Actions-fouten.

Als een test faalt op de main branch of in de merge queue, leest een workflow de logs, zoekt naar bewijs van flakiness en kijkt naar retries, timing en omgevingsfactoren. Als het bewijs een specifieke reparatie ondersteunt, wordt de test of implementatie bijgewerkt, een pull request geopend en de volledige CI opnieuw gedraaid.

Slagen bij een retry maakt de oorspronkelijke fout niet onschadelijk. Teams die op de retry-knop vertrouwen, verliezen geleidelijk het vertrouwen in rode builds. Zodra dat gebeurt, worden mislukte checks een nieuwe vorm van achtergrondruis.

Een geautomatiseerde reparatieworkflow maakt van een intermitterende fout een traceerbare codewijziging. Diagnose, patch en verificatie blijven zichtbaar in de pull request. Engineers kunnen risicovollere wijzigingen inspecteren, terwijl kleine fixes met sterk bewijs via de merge queue kunnen.

Ons kwaliteitssysteem kent momenteel drie brede lagen:

FaseMechanismenTypische aandachtspunten
Tijdens schrijvenTypeScript, contracten, Drizzle, ESLinttypefouten, interface-drift, bekende codepatronen
Voor mergenKnip, integratietests, echte databases, previews, merge queuedode code, modulegedrag, volledige runtime-resultaten
Na mergengeplande en event-gedreven vm0-workflowsAI slop, semantische antipatterns, flaky tests, architectuurdrift

De lagen voeden elkaar. Problemen gevonden door workflows kunnen statische regels worden. Fouten in CI of productie kunnen tests en nieuwe engineeringrichtlijnen opleveren.

Waar menselijke aandacht naartoe gaat bij review van AI-gegenereerde code

vm0-engineers lezen nog steeds code, vooral bij architectuurwijzigingen, beveiligingsgevoelig werk, betalingen en datamigraties. "Nooit code lezen" is geen teamregel geworden.

Wat veranderde is de verdeling van aandacht bij code review. Code lezen is één signaal naast contracten, testgrenzen, previewgedrag, screenshots, kwaliteitsmetingen en workflowdiagnostiek.

Verschillende beslissingen vereisen nog steeds ervaring en oordeel:

  • of de requirement compleet is
  • waar modulegrenzen moeten liggen
  • welke fouten herstelbaar zijn
  • hoeveel bedrijfsimpact een fout kan hebben
  • of een beveiligingsmodel passend is
  • welk nieuw foutpatroon de huidige regels niet afdekken

Een vergelijkbare verschuiving vond plaats aan de designkant, waar design-as-code visuele beslissingen in dezelfde repository en reviewflow bracht. Engineers onderhouden ook de omgeving rond de agents. Als een probleem zich herhaalt, besluiten we of er een type constraint, lintregel, test of terugkerende workflow bij moet. Het ontwikkelsysteem is een belangrijk engineeringartefact op zich geworden.

Leesbare code blijft belangrijk. De volgende lezer kan een engineer of een agent zijn. Verwarde code verbruikt meer context, vergroot de scope van toekomstige wijzigingen en maakt verificatie minder betrouwbaar.

Security en de wijzigingen die niet versnellen

Snelheid geldt niet overal. vm0 houdt een korte lijst wijzigingen die hun eigen tempo volgen: beveiligingsgevoelig werk, betalingen, datamigraties en architectuurbeslissingen. Een engineer leest die regel voor regel, ongeacht wie of wat ze schreef.

Het beveiligingsrisico van AI-gegenereerde code is volgens onze ervaring minder een exotische kwetsbaarheid dan plausibele code waar niemand eigenaar van is. De controles die ertoe doen zijn gewone, consequent toegepaste maatregelen:

  • Unittests, die we verder spaarzaam inzetten, worden bewust gebruikt voor beveiligingsgevoelige logica, algoritmes en statemachines.
  • Tests mocken alleen bij externe-systeemgrenzen. Interne infrastructuur blijft echt, zodat een wijziging die een intern contract breekt in CI faalt en niet pas in productie.
  • Agents werken in de dev-container tegen een per-pull-request databasebranch, niet op de machine van een ontwikkelaar en niet op een gedeelde database.
  • De merge queue en verplichte checks kennen geen routinematige omzeiling, ook niet voor een pull request die door een agent is geopend en voor automatisch mergen is gemarkeerd.
  • try/catch is standaard beperkt, zodat een fout minder snel wordt weggemoffeld door een fallback die een agent toevoegde om een pad te laten lopen.

Credential handling is een apart ontwerpprobleem met een eigen oplossing: we beschreven het broker pattern dat tokens uit handen van agents houdt in een apart artikel.

Dit alles maakt gegenereerde code niet vanzelf veilig. Het verkleint de set wijzigingen waar een menselijke beslissing de enige controle is, en maakt die set expliciet.

Wat deze cijfers niet laten zien

Het wekelijkse rapport toont de schaal van de repository en de snelheid van oplevering. Het toont niet vanzelf de betrouwbaarheid in productie.

Het effect op runtime-kwaliteit vereist beschikbaarheid, productiefoutpercentages, incidentenaantallen, change-failure rates, rollbackfrequentie en mean time to recovery. Een groene CI-run beschrijft één deel van het leveringsproces.

We blijven die uitkomstmaten verzamelen. Engineeringpraktijken verklaren hoe een systeem risico beheert; productiedata toont hoe goed dat beheer werkt.

FAQ

Wat is vibe coding? Vibe coding is een LLM om code vragen, uitvoeren wat het oplevert, om aanpassingen vragen en de gegenereerde code niet lezen. Martin Fowler's definitie is bewust smal: de ontwikkelaar moet "vergeten dat de code überhaupt bestaat." Het past bij prototypes, wegwerpsoftware en tools met beperkte gevolgen.

Wat is agentic coding? Agentic coding is een langduriger vorm van AI-ondersteunde ontwikkeling waarbij een agent een repository leest, bestanden bewerkt, tests uitvoert en zelfstandig iteraties maakt. Mensen blijven eigenaar van architectuur en gedrag, en beoordelen het bewijs van een run in plaats van elke regel code.

Wat is het verschil tussen vibe coding en agentic coding? Aandacht, niet auteurschap. Bij vibe coding wordt de code nooit geïnspecteerd. Bij agentic coding werkt de agent zelfstandig terwijl engineers het bewijs eromheen controleren: testresultaten, kwaliteitssignalen, previews en productiegedrag. vm0 wordt meestal vibe-coded genoemd; in Fowlers termen is het agentic coding.

Wat is AI slop? AI slop is het residu dat AI-gegenereerde code achterlaat: onnodige fallbacks, dubbele abstracties, tests die een publieke grens omzeilen, defensieve branches voor onmogelijke staten. Elk geval lijkt onschuldig. Gezamenlijk maken ze een repository moeilijker te begrijpen en geven ze de volgende agent slechte voorbeelden.

Hoe review je AI-gegenereerde code bij 630 pull requests per week? Niet regel voor regel. Bij vm0 gaat menselijke review naar waar oordeel nodig is: architectuur, beveiligingsgevoelig werk, betalingen, datamigraties. De rest wordt gedragen door mechanismen: strikte types en contracten, integratietests op modulegrenzen, echte databases in per-pull-request previews, browserverificatie, Knip en een merge queue die geen wijziging routinematig omzeilt.

Wat zijn de best practices voor vibe coding op schaal? Vijf houden stand na acht maanden bij vm0: trek de ontwikkelomgeving gelijk zodat agents en mensen dezelfde commando's draaien; maak standaarden uitvoerbaar in types, linters en CI in plaats van documenten; test op modulegrenzen tegen echte infrastructuur; geef agents een volledige feedbackloop inclusief browser; en ruim continu op in plaats van af en toe grootschalig.

Wat zijn de beveiligingsrisico's van AI-gegenereerde code? Het gewone risico is geen exotische kwetsbaarheid, maar plausibele code waar niemand eigenaar van is. vm0 houdt beveiligingsgevoelig werk, betalingen en datamigraties op menselijke review, gebruikt unittests bewust voor die logica, houdt interne infrastructuur echt in tests, beperkt patronen zoals try/catch die fouten verbergen, en staat geen routinematige omzeiling van verplichte checks toe.

Creëert AI-gegenereerde code technische schuld? Het creëert een specifiek soort: code die nog compileert en tests doorstaat, maar niet meer deelneemt aan het systeem, plus semantisch residu dat geen enkele linter kan benoemen. Knip verwijdert het deterministische deel. Terugkerende workflows pakken de rest aan in kleine dagelijkse batches, en een patroon dat vaak genoeg terugkomt wordt een lintregel of type constraint.

Hoeveel van de vm0-codebase is door AI geschreven? Het grootste deel van de implementatie. Zes engineers schreven 556 van de 630 pull requests die in de week van 20–26, 2026 werden gemerged, met release-automatisering voor de rest, en agents schreven het grootste deel van de code in die pull requests. Wat de engineers bezitten is de architectuur, de beperkingen en het productiegedrag.

Wat is een testing trophy, en waarom geen testpiramide? Een testing trophy zet statische analyse aan de basis, integratietests als dominante laag en een klein aantal end-to-end tests bovenop. vm0 gebruikt dit omdat tests via de publieke grens van een module het gedrag blijven beschermen terwijl een agent de implementatie eronder herstructureert, wat een grote unittests-laag niet doet.

Hoe vind je dode code in een AI-geschreven repository? Codegeneratie voegt bestanden en abstracties toe; verwijderen vereist meestal een aparte prompt. vm0 draait Knip als vaste check om ongebruikte bestanden, exports, afhankelijkheden en entry points te vinden. TypeScript bevestigt dat code geldig is; Knip vraagt of het nog deelneemt aan het systeem, en dat is de relevante vraag voor dode code.

Is vibe-coded code veilig om in productie te draaien? Dat hangt af van wat het verifieert, niet van wie het typte. De vereiste signalen zijn niet veranderd: types en contracten, tests via publieke grenzen, echte infrastructuur en een pipeline die groen moet zijn. De cijfers in dit artikel beschrijven de schaal van de repository en de snelheid van oplevering. Beschikbaarheid, foutpercentages en change-failure rates zijn de maatstaven die de productievraag beantwoorden, en we verzamelen die nog.

Acht maanden verder

Acht maanden is te vroeg voor een definitieve methode. Modellen, agenttools en de repository blijven veranderen, en onze regels en workflows veranderen mee.

Eén verschuiving is al duidelijk. Naarmate codegeneratie sneller werd, namen de omgeving, beperkingen, tests en feedbacksysteem meer van de kwaliteitslast over. Engineers besteden minder tijd aan het typen van implementatie en meer aan het definiëren van gedrag, het ontwerpen van grenzen en het verbeteren van verificatie.

De vm0-codebase zal blijven groeien. Knip verwijdert deterministisch residu. Statische regels blokkeren foutpatronen die we al begrijpen. Integratietests beschermen modulegedrag. Terugkerende workflows pakken degradatie aan die we nog niet mechanisch kunnen uitdrukken.

Code blijft belangrijk. De best practices in dit artikel gaan niet over minder code schrijven, maar over bepalen wat waar moet zijn voordat een wijziging op de main branch thuishoort. We gebruiken nu meer machine-uitvoerbaar bewijs om te beslissen of een wijziging op de main branch hoort, en of de implementatie in de repository moet blijven.

Stay in the loop

// Get the latest insights on AI teammates and collaboration.