Back to all posts

1.3 millioner linjer, 630 PR-er i uken: Slik holder vi en vibe-kodet kodebase vedlikeholdbar

1.3 millioner linjer, 630 PR-er i uken: Slik holder vi en vibe-kodet kodebase vedlikeholdbar

I juli 2026 skrev Robert C. Martin at han ikke lenger leser koden produsert av sine kodeagenter.

Uttalelsen fikk mye oppmerksomhet. Resten av innlegget hans er viktigere for å forstå hvordan han jobber. Martin omgir agentene med enhetstester, Gherkin-akseptansetester, QA-prosedyrer, mutasjonstesting, dekningskontroller og kvalitetsmålinger. Koden får hans tillit ved å passere gjennom dette systemet.

Han hadde gjort et lignende poeng noen måneder tidligere. I stedet for å inspisere implementasjonen, ser han på testdekning, avhengighetsstruktur, syklomatisk kompleksitet, modulstørrelse og mutasjonstestingsresultater. Da folk tolket dette som å gi opp gjennomgang helt, presiserte han: «Jeg gjennomgår mye — bare ikke koden.»

Kommentarene hans beskriver et praktisk problem i AI-tung programvareutvikling. Kodeagenter kan produsere endringer raskere enn folk kan lese dem. Når volumet av AI-generert kode overstiger teamets gjennomgangskapasitet, kan linje-for-linje-inspeksjon ikke lenger være den eneste kilden til tillit.

Vi har håndtert det samme problemet i vm0 de siste åtte månedene. Mesteparten av implementasjonen i vårt repository er skrevet av agenter — vibe-koding, i populært kortformat — mens seks ingeniører fortsatt er ansvarlige for hva koden gjør i produksjon.

Kortversjonen

vm0 er en vibe-kodet kodebase: agenter skriver mesteparten av implementasjonen, og seks ingeniører eier det som skjer i produksjon. Etter åtte måneder inneholder repositoryet 1,329,170 linjer og fikk inn 630 pull requests på én uke. Fem mekanismer bærer kvalitetsbyrden som linje-for-linje-gjennomgang ikke lenger kan bære alene.

  • Konvergerte miljøer. Ingeniører og agenter jobber i samme dev-container, og hver pull request kan få sin egen databasegren. En kommando som fungerte for en agent, fungerer for en person.
  • Kjørbare begrensninger. Streng TypeScript, omtrent 135 typede API-kontraktmoduler, Oxlint, ESLint og arkitekturregler, med CI som ikke godtar advarsler. En regel som kan sjekkes maskinelt, overlates ikke til gjennomgang.
  • Grensesnittester. En test-trofe i stedet for en testpyramide: integrasjonstester gjennom offentlige modulgrenser, ekte PostgreSQL og ekte migreringer, mocks kun mot eksterne systemer. Testkode utgjør omtrent 36 prosent av repositoryet.
  • Fullstendige tilbakemeldingssløyfer. Agenter starter applikasjonen og databasen, kjører migreringer og styrer en ekte nettleser, og rapporterer deretter kommandoene de kjørte, stien de testet og skjermbilder. Den typiske pull request merges etter ca. 53 minutter.
  • Kontinuerlig opprydding. Knip fjerner død kode deterministisk, daglige arbeidsflyter rydder AI-rot ingen linter kan navngi, og et mønster som gjentar seg ofte nok, blir til en lint-regel eller en typebegrensning.

Hva vibe-koding og agentisk koding faktisk betyr

Vibe-koding er å be et LLM om kode, kjøre det som produseres, be om endringer og ikke bry seg om den genererte koden. Martin Fowlers definisjon er bevisst smal, og han fremhever uttrykket «glem at koden i det hele tatt eksisterer». Det passer for prototyper, engangsprogramvare og små verktøy med begrensede konsekvenser.

Agentisk koding, som Fowler kaller agentic programming, er det team gjør når de forventer å vedlikeholde resultatet. En agent leser repositoryet, redigerer filer, kjører tester og jobber selvstendig over lengre tid, mens folk beholder eierskap til struktur og oppførsel og vurderer bevisene en kjøring produserer: testresultater, kvalitetssignaler, forhåndsvisninger og produksjonsoppførsel.

Forskjellen handler ikke om hvor mye kode en modell skriver. Det handler om hvor menneskelig oppmerksomhet rettes.

Vibe-kodingAgentisk koding
Agentens autonomiPrompt, kjør, prompt igjenLeser repository, redigerer, tester, itererer
Hvem leser kodenIngenFolk leser det som innebærer risiko
Hva verifiseresOm utdata ser riktige utTyper, kontrakter, tester, forhåndsvisninger, produksjonssignaler
Best egnet forPrototyper og engangsverktøyProgramvare med vedlikeholdshorisont
Typisk feilKode ingen forstårVerifikasjon for svak for volumet

Agenter som redigerer et repository er én gren av et bredere skifte; vi har sett på ikke-utviklersiden av dette separat.

Vi omtaler ofte vm0 som et vibe-kodet prosjekt fordi det har blitt vanlig kortform for programvare skrevet hovedsakelig av AI. I Fowlers termer er det nærmere agentisk koding. Våre ingeniører skriver mindre av implementasjonen enn før, men eier fortsatt arkitektur, vedlikeholdskostnad og produksjonsoppførsel.

Etter hvert som kodegenerering ble raskere, flyttet mer av ansvaret inn i utviklingsmiljøet, typesystemet, testpakken og automatiserte vedlikeholdsarbeidsflyter.

Åtte måneders vekst i en vibe-kodet kodebase

vm0-repositoryet ble opprettet i november 2025. Ved tidspunktet for vår siste ukentlige ingeniørskalarapport var det omtrent åtte og en halv måned gammelt.

Repositoryet inneholdt:

MetrikkAntall
Ikke-tomme logiske linjer1,329,170
Produksjonslinjer850,913
Testlinjer478,257
Kildefiler5,549
Testfiler1,327
Pakker44
Commits på hovedgrenen14,005

Disse tallene kommer fra vår ukentlige ingeniørskalarapport for uken 20–26, 2026, målt direkte fra monorepoet. Testkode utgjorde omtrent 36 prosent av den målte kodebasen. Det er en andel av kodevolumet, ikke en testdekningsfigur, men gir et inntrykk av hvor mye implementasjon som har samlet seg rundt verifikasjon.

I løpet av tre nylige hele uker gjorde seks ingeniører henholdsvis 541, 640 og 631 commits. I uken 20–26 registrerte GitHub 630 innfusede pull requests. Seks ingeniører forfattet 556 av dem, mens utgivelsesautomatisering sto for de resterende 74.

Median tid fra åpning til merging av en pull request var omtrent 53 minutter. 90-persentilen var omtrent 7.4 timer.

Med denne endringstakten er menneskelig gjennomgang fortsatt nyttig, men kan ikke bære hele kvalitetssystemet. Vi trenger uavhengige signaler på flere punkter i endringslivssyklusen.

Vår nåværende tilnærming har fem gjennomgående temaer: konvergerte miljøer, kjørbare begrensninger, grensesnittester, fullstendige tilbakemeldingssløyfer og kontinuerlig opprydding.

Konvergerende utviklingsmiljø med dev-containere

Mange vanskelig-reproduserbare feil skyldes tilstand som aldri vises i en pull request.

En utvikler kan ha en udokumentert miljøvariabel, et globalt installert verktøy, en gammel konfigurasjonsfil eller en database som har kjørt i månedsvis. Folk venner seg til disse detaljene. Agenter ser dem vanligvis ikke.

Vi sluttet gradvis å behandle vertsmaskinen som standard utviklingsmiljø. Ingeniører og agenter jobber i en dev-container. Utviklingsbildet inkluderer prosjektets verktøykjede, PostgreSQL, pgvector, Chromium og nettleserautomatiseringsverktøy.

CI kjører versjonerte verktøykjede-bilder bygget fra samme multi-stage Dockerfile-familie. Utviklings- og CI-bilder har ulike formål, og produksjon har sin egen distribusjonsform. Den nyttige egenskapen er konvergens: verktøyversjoner, avhengigheter og kjøretidsforutsetninger er eksplisitte og versjonerte.

Dette gir oss en enkel forventning. Kommandoer som en agent kjører, skal kunne reproduseres av en ingeniør i samme utviklingscontainer. Sjekker som passerer under utvikling, skal kjøres under en nært beslektet verktøykjede i CI.

Vi bruker en lignende idé for data. Hver pull-request-forhåndsvisning kan ha sin egen databasegren og kjøre ekte migreringer og seed-steg. En agent kan opprette data, endre dem og gjenta destruktive tester uten å låne en utviklers eksisterende database eller arve tilstand fra en annen pull request.

Miljøendringer går gjennom repositoryet. Verktøyoppgraderinger, nettleserversjoner og databaseutvidelser gjennomgås og distribueres på samme måte som applikasjonskode.

Gjøre ingeniørstandarder kjørbare i typer og lint-regler

Skriftlige standarder hjelper folk å forstå designbeslutninger. De garanterer i mindre grad at hver endring følger disse beslutningene, spesielt under lange agentsesjoner.

Vi legger stabile regler inn i typesystemet, linters og CI når regelen kan uttrykkes pålitelig.

vm0 bruker strenge TypeScript-innstillinger, inkludert strict og noUncheckedIndexedAccess. Noen pakker aktiverer ekstra sjekker for ubrukte verdier og implisitte returverdier.

API-et bruker et skjema-først typet REST-kontraktslag bygget med tRPCs typemekanisme og Zod-skjemaer. Drizzle kobler databaseaksess til TypeScript-typer. På tidspunktet for denne gjennomgangen inneholdt repositoryet omtrent 135 kontraktmoduler.

Disse valgene fanger ikke opp gale produktbeslutninger. De avdekker grensesnittsdrift tidlig. Når et felt eller svar endres, feiler tilknyttede kall ofte under statisk analyse. Feilmeldingen er vanligvis spesifikk nok til at en agent kan bruke den i neste iterasjon.

Linting dekker en annen gruppe regler. Plattformen kjører Oxlint, typebevisste sjekker og ESLint, sammen med prosjekttilpassede arkitekturregler. CI godtar ingen advarsler. Advarsler som kan bli liggende, blir bakgrunnsstøy, og bakgrunnsstøy er lett for både folk og agenter å overse.

Når et problem gjentar seg, vurderer vi hvor sjekken hører hjemme:

  1. Kan typesystemet uttrykke det?
  2. Kan en linter eller et strukturverktøy oppdage det nøyaktig?
  3. Krever det semantisk vurdering på tvers av repositoryet?

De to første gir rask, deterministisk tilbakemelding på hver endring. Vi håndterer den tredje gruppen med gjentakende arbeidsflyter beskrevet senere i artikkelen.

Innsnevring av feilutsatte mønstre i AI-generert kode

Noen språkfunksjoner har legitime bruksområder og dukker også ofte opp i skjør agentgenerert kode.

try/catch kan viske ut en feilgrense. Når en agent møter en feil, er det lett å legge til en catch-blokk og fallback for å holde stien i gang, men da mister man den opprinnelige feilen. Å blande Promise .then() og .catch() med async/await kan spre kontrollflyten over flere stiler.

React sin useEffect skaper et lignende problem i tilstandshåndtering. Den brukes ofte til å kopiere tilstand, synkronisere to sannhetskilder eller kode en rekkefølgeavhengighet som er vanskelig å se fra datamodellen.

Kjerneplattformen for web begrenser disse mønstrene som standard. Et begrunnet unntak kan bli stående, med en eksplisitt forklaring ved siden av.

Produksjonskoden i kjerneplattformen inneholder for øyeblikket ingen useEffect. Vi bruker ccstate og andre effektløse mønstre for å modellere avhengigheter og sideeffekter mer eksplisitt. Et lite antall produksjons-useEffect-kall finnes fortsatt andre steder i monorepoet, hovedsakelig i delt UI- og desktopkode, så påstanden gjelder innenfor dette omfanget.

Disse reglene vokste frem av gjentakende feil i dette repositoryet. Når et mønster gjentatte ganger skaper det samme vedlikeholdsproblemet, flytter vi det fra gjennomgangsveiledning til en kjørbar begrensning.

Testing på modulgrenser: et test-trofe, ikke en pyramide

vm0 følger ikke en konvensjonell testpyramide. Vår testveiledning beskriver et test-trofe: statisk analyse i bunnen, integrasjonstester som dominerende lag og et lite antall ende-til-ende-tester for kritiske brukerreiser.

Enhetstester er relativt uvanlige. Vi bruker dem selektivt for sikkerhetskritisk logikk, algoritmer og tilstandsautomater. Mesteparten av forretningslogikken testes gjennom en moduls offentlige grense.

API-tester kaller den ekte applikasjonen gjennom dens kontrakter. Testdata forberedes gjennom produksjonsendepunkter der det er praktisk, og påstander gjøres gjennom offentlig oppførsel. Tester unngår å nå direkte inn i en intern tjeneste eller endre databasetabeller, fordi det kobler dem til gjeldende implementasjon.

Intern infrastruktur forblir ekte der kostnaden er rimelig:

  • PostgreSQL og pgvector
  • databasemigreringer
  • filsystemet
  • interne tjenester
  • mocks ved eksterne systemgrenser

Disse integrasjonstestene kjører tregere enn sterkt isolerte enhetstester og krever et mer komplett miljø. De reduserer også gapet mellom «testen passerte» og «applikasjonen kan utføre denne operasjonen mot en ekte database».

Grensetester gir rom for store interne refaktoreringer. En agent kan omorganisere moduler, splitte en tjeneste eller endre dataaksesslaget mens den offentlige oppførselen forblir beskyttet.

Uncle Bob foretrekker en annen miks, med tung bruk av enhetstester, Gherkin og mutasjonstesting. Det felles prinsippet er uavhengig verifikasjon. Prosessen som genererte koden, skal ikke være den eneste kilden som hevder at koden fungerer.

Gi kodeagenter en komplett tilbakemeldingssløyfe

Våre tidlige kodeagent-kjøringer endte ofte med en kjent rapport: koden ble endret og TypeScript bestod; vennligst start applikasjonen og sjekk siden.

Det etterlater halve utviklingssløyfen hos ingeniøren. Noen må fortsatt forberede en database, kjøre tjenestene, åpne en nettleser, opprette data, observere feilen og beskrive den tilbake til agenten.

Agenter har nå nok av utviklingsmiljøet til å utføre mer av dette arbeidet. De kan starte applikasjonen og databasen, kjøre migreringer, opprette testdata, besøke en pull-request-forhåndsvisning og utføre ekte nettleserinteraksjoner.

Nettleserverifisering fanger en klasse problemer som typesjekker og API-tester ikke dekker godt: ødelagt navigasjon, lastingstilstander som aldri avsluttes, tillatelsesfeil som kun vises i en komplett flyt og visuelle regresjoner.

Etter en kjøring rapporterer agenten kommandoene den utførte, stien den testet og hva den observerte. For brukergrensesnittendringer kan den inkludere skjermbilder. En ingeniør kan inspisere dette beviset før han eller hun bestemmer seg for å åpne forhåndsvisningen personlig.

Et skjermbilde er ikke en test og beviser ikke fravær av andre feil. Det senker kostnaden for å rekonstruere agentens kontekst. For en liten grensesnittendring er reproduserbare steg og et sluttbilde mye mer nyttig enn en melding om at endringen «bør være fikset».

Kvaliteten på agentens arbeid avhenger sterkt av tilbakemeldingen den kan få uten å vente på en person.

Holde grener korte med trunk-basert utvikling

Rask kodegenerering kan produsere et stort antall grener.

Langlivede funksjonsgrener samler opp flettingskonflikter, duplisert arbeid og utdatert kontekst. Etter hvert som pull requests vokser, blir gjennomgang vanskeligere og tregere. Vi bruker trunk-basert utvikling for å holde grenene korte og integrere kontinuerlig rundt main.

Våre hovedgrenregler krever:

  • pull requests for endringer
  • lineær historikk og squash merges
  • en flettekø
  • Turbo-, Rust- og sikkerhetssjekker
  • ingen rutinemessig omgåelse av påkrevde porter

Automatisering følger samme vei. En agent kan opprette en pull request, og noen lavrisiko vedlikeholdsoppgaver kan tillate automatisk flette, men endringen går fortsatt gjennom CI og flettekøen.

Størrelsen og levetiden til pull requests forklarer hvordan 630 av dem kunne merges på én uke. En liten endring har mindre kontekst, er lettere å verifisere og mindre sannsynlig å komme i konflikt med produktarbeid eller en annen automatisk reparasjon.

Knip som repository-garbage collection: fjerning av død kode

Kodegenerering legger naturlig til filer og abstraksjoner. Sletting krever ofte en egen prompt.

Etter en refaktorering kan gamle filer bli liggende i repositoryet. Fjerning av en funksjon kan etterlate eksporteringer, avhengigheter og inngangspunkter. Disse artefaktene ødelegger sjelden tester, men gjør kodebasen vanskeligere å navigere over tid.

Vi bruker Knip for å finne ubrukte filer, eksporteringer, avhengigheter og inngangspunkter. TypeScript kan bekrefte at koden er gyldig; Knip spør om den fortsatt deltar i systemet.

Resten har en ekstra kostnad for kodeagenter. Repositoryet er en av deres viktigste kontekstkilder. En foreldet hjelper eller forlatt implementasjon kan se ut som et godkjent mønster for neste agent som leser det. Å holde denne konteksten ren er den lite glamorøse delen av kontekstingeniørarbeidet: det en agent leser fra repositoryet, er like mye et input som prompten den fikk.

Å fjerne død kode forbedrer også inputene for fremtidige kjøringer. Knip håndterer den deterministiske delen av dette arbeidet raskt nok til å bli en fast kvalitetskontroll.

Gjentakende AI-rot-oppryddingsarbeidsflyter

Knip og ESLint har klare begrensninger. Mange former for forringelse krever prosjektkontekst og semantisk vurdering.

Vi bruker «AI-rot» som en praktisk betegnelse for dette restproduktet: unødvendige fallbacks, dupliserte abstraksjoner, tester som omgår en offentlig grense, eller defensive grener for umulige tilstander. Hver enkelt forekomst kan virke harmløs. Samlet gjør de repositoryet vanskeligere å forstå og gir fremtidige agenter dårlige eksempler å følge.

Flere gjentakende vm0-arbeidsflyter skanner etter disse mønstrene på en tidsplan.

En daglig AI-rot-opprydding ser etter nytt restprodukt og velger et lite sett med sikre, lavrisiko rettelser. Andre arbeidsflyter inspiserer API-tester som når inn i interne tjenester, React- og ccstate-antimønstre og teknisk gjeld som kan håndteres trygt.

Hver arbeidsflyt holder endringene smale. Den åpner en pull request, og stoler deretter på vanlige typesjekker, lint-regler, tester og flettekø. En pull request konfigurert for automatisk flette må fortsatt passere de samme portene.

Å kjøre agenter på en tidsplan er ikke gratis, og vi har skrevet separat om hvordan holde disse kostnadene nede. Arbeidsflytene forsøker ikke å rydde all teknisk gjeld i én omgang. En liten daglig batch er lettere å verifisere og mindre forstyrrende enn en stor opprydding hvert par måneder.

Når en gjentakende arbeidsflyt finner det samme mønsteret ofte nok, vurderer vi å flytte sjekken inn i ESLint, Knip eller typesystemet. Den semantiske arbeidsflyten fungerer som et sted å observere og forbedre regelen før den blir en billigere deterministisk sjekk.

Gjøre ustabile testfeil til automatiske reparasjoner

En annen gruppe arbeidsflyter starter fra feil i GitHub Actions.

Når en test feiler på hovedgrenen eller i flettekøen, leser en arbeidsflyt loggene, ser etter tegn på ustabilitet og sjekker forsøk på nytt, timing og miljøfaktorer. Hvis bevisene støtter en spesifikk reparasjon, oppdaterer den testen eller implementasjonen, åpner en pull request og kjører hele CI-løpet på nytt.

Å passere på et nytt forsøk gjør ikke den opprinnelige feilen ufarlig. Team som stoler på retry-knappen, mister gradvis tilliten til røde bygg. Når det skjer, blir feilede sjekker en ny form for bakgrunnsstøy.

En automatisert reparasjonsarbeidsflyt gjør en sporadisk feil til en sporbar kodeendring. Diagnosen, patchen og verifikasjonen forblir synlig i pull requesten. Ingeniører kan inspisere høyrisikoendringer, mens smale rettelser med sterke bevis kan gå videre gjennom flettekøen.

Vårt kvalitetssystem har for tiden tre brede lag:

FaseMekanismerTypiske bekymringer
Under skrivingTypeScript, kontrakter, Drizzle, ESLinttypefeil, grensesnittsdrift, kjente kodemønstre
Før fletteKnip, integrasjonstester, ekte databaser, forhåndsvisninger, flettekødød kode, moduloppførsel, komplette kjøretidsresultater
Etter fletteplanlagte og hendelsesdrevne vm0-arbeidsflyterAI-rot, semantiske antimønstre, ustabile tester, arkitekturell drift

Lagene mater hverandre. Problemer funnet av arbeidsflyter kan bli statiske regler. Feil funnet i CI eller produksjon kan bli tester og ny ingeniørveiledning.

Hvor menneskelig oppmerksomhet rettes i gjennomgang av AI-generert kode

vm0-ingeniører leser fortsatt kode, spesielt for arkitekturendringer, sikkerhetskritisk arbeid, betalinger og datamigreringer. Vi har ikke gjort «aldri les koden» til en teamregel.

Det som har endret seg, er fordelingen av oppmerksomhet i kodegjennomgang. Kodelesing er ett signal blant kontrakter, testgrenser, forhåndsvisningsatferd, skjermbilder, kvalitetsmålinger og arbeidsflytdiagnostikk.

Flere beslutninger krever fortsatt erfaren vurdering:

  • om kravet er komplett
  • hvor modulgrenser bør gå
  • hvilke feil som kan håndteres
  • hvor stor forretningspåvirkning en feil kan få
  • om en sikkerhetsmodell er passende
  • hvilket nytt feil-mønster dagens regler ikke dekker

Et lignende skifte skjedde på designsiden, der design-as-code flyttet visuelle beslutninger inn i samme repository og samme gjennomgangsløp. Ingeniører vedlikeholder også miljøet rundt agentene. Når et problem gjentar seg, avgjør vi om vi skal legge til en typebegrensning, lint-regel, test eller gjentakende arbeidsflyt. Utviklingssystemet har blitt et viktig ingeniørartefakt i seg selv.

Lesbar kode er fortsatt viktig. Neste leser kan være en ingeniør eller en annen agent. Sammenfiltret kode bruker mer kontekst, utvider omfanget av fremtidige endringer og gjør verifikasjon mindre pålitelig.

Sikkerhet og endringene som ikke går raskere

Fart gjelder ikke alt. vm0 har en kort liste over endringer som går i sitt eget tempo: sikkerhetskritisk arbeid, betalinger, datamigreringer og arkitekturbeslutninger. En ingeniør leser disse linje for linje, uansett hvem eller hva som har skrevet dem.

Sikkerhetsrisikoen ved AI-generert kode, etter vår erfaring, handler mindre om eksotiske sårbarheter enn om plausibel kode ingen eier. Kontrollene som betyr noe, er vanlige, brukt konsekvent:

  • Enhetstester, som vi ellers bruker sparsomt, brukes bevisst for sikkerhetskritisk logikk, algoritmer og tilstandsautomater.
  • Tester mocker kun ved eksterne systemgrenser. Intern infrastruktur forblir ekte, slik at en endring som bryter en intern kontrakt, feiler i CI og ikke i produksjon.
  • Agenter jobber i dev-container mot en databasegren per pull request, ikke på utviklerens maskin og ikke mot en delt database.
  • Flettekøen og påkrevde sjekker har ingen rutinemessig omgåelse, også for pull requests en agent har åpnet og merket for automatisk flette.
  • try/catch er begrenset som standard, så en feil blir mindre sannsynlig svelget av en fallback en agent har lagt til for å holde en sti i gang.

Håndtering av legitimasjon er et eget designproblem med egen løsning: vi beskrev megler-mønsteret som holder tokens unna agentens hender i en egen artikkel.

Ingenting av dette gjør generert kode trygg i seg selv. Det snevrer inn settet av endringer der et menneskelig valg er eneste kontroll, og gjør dette settet eksplisitt.

Hva disse tallene ikke viser

Den ukentlige rapporten viser repositoryets størrelse og leveringshastighet. Den viser ikke i seg selv produksjonspålitelighet.

Å vurdere effekten på kjøretidskvalitet krever tilgjengelighet, produksjonsfeilrater, hendelsestall, endringsfeilrater, tilbakeføringsfrekvens og gjennomsnittlig tid til gjenoppretting. En grønn CI-kjøring beskriver én del av leveringsprosessen.

Vi fortsetter å samle disse resultatmålingene. Ingeniørpraksis forklarer hvordan et system håndterer risiko; produksjonsdata viser hvor godt denne håndteringen fungerer.

FAQ

Hva er vibe-koding? Vibe-koding er å be et LLM om kode, kjøre det som returneres, be om endringer og ikke lese den genererte koden. Martin Fowlers definisjon er med vilje smal: utvikleren skal «glemme at koden i det hele tatt eksisterer». Det passer for prototyper, engangsprogramvare og verktøy med begrensede konsekvenser.

Hva er agentisk koding? Agentisk koding er en mer langvarig form for AI-assistert utvikling der en agent leser et repository, redigerer filer, kjører tester og itererer selvstendig over tid. Folk eier fortsatt arkitektur og oppførsel, og de vurderer bevisene en kjøring produserer, i stedet for hver linje agenten skrev.

Hva er forskjellen på vibe-koding og agentisk koding? Oppmerksomhet, ikke forfatterskap. I vibe-koding inspiseres aldri koden. I agentisk koding jobber agenten selvstendig mens ingeniører sjekker bevisene rundt: testresultater, kvalitetssignaler, forhåndsvisninger og produksjonsoppførsel. vm0 omtales vanligvis som vibe-kodet; i Fowlers termer er det agentisk koding.

Hva er AI-rot? AI-rot er restproduktet AI-generert kode etterlater: unødvendige fallbacks, dupliserte abstraksjoner, tester som omgår en offentlig grense, defensive grener for umulige tilstander. Hver enkelt ser harmløs ut. Samlet gjør de et repository vanskeligere å forstå og gir neste agent dårlige eksempler å følge.

Hvordan gjennomgår dere AI-generert kode ved 630 pull requests i uken? Ikke linje for linje. I vm0 går menneskelig gjennomgang dit vurdering kreves: arkitektur, sikkerhetskritisk arbeid, betalinger, datamigreringer. Alt annet bæres av mekanismer: strenge typer og kontrakter, integrasjonstester på modulgrenser, ekte databaser i per-pull-request-forhåndsvisninger, nettleserverifisering, Knip og en flettekø ingen endring rutinemessig omgår.

Hva er beste praksis for vibe-koding i stor skala? Fem holder stand etter åtte måneder i vm0: konverger utviklingsmiljøet slik at agenter og folk kjører samme kommandoer; gjør standarder kjørbare i typer, linters og CI i stedet for dokumenter; test på modulgrenser mot ekte infrastruktur; gi agenter en komplett tilbakemeldingssløyfe inkludert nettleser; og rydd kontinuerlig i stedet for i store sjeldne omganger.

Hva er sikkerhetsrisikoen ved AI-generert kode? Den vanlige risikoen er ikke en eksotisk sårbarhet, men plausibel kode ingen eier. vm0 holder sikkerhetskritisk arbeid, betalinger og datamigreringer på menneskelig gjennomgang, bruker enhetstester bevisst for slik logikk, holder intern infrastruktur ekte i tester, begrenser mønstre som try/catch som svelger feil, og tillater ingen rutinemessig omgåelse av påkrevde sjekker.

Skaper AI-generert kode teknisk gjeld? Den skaper en bestemt type: kode som fortsatt kompilerer og passerer tester, men ikke lenger deltar i systemet, pluss semantisk restprodukt ingen linter kan navngi. Knip fjerner den deterministiske delen. Gjentakende arbeidsflyter håndterer resten i små daglige batcher, og et mønster som gjentar seg ofte nok, blir til en lint-regel eller typebegrensning.

Hvor mye av vm0-kodebasen er skrevet av AI? Mesteparten av implementasjonen. Seks ingeniører forfattet 556 av de 630 pull requests som ble fusjonert i uken 20–26, 2026, med utgivelsesautomatisering som forfattet resten, og agenter skrev hoveddelen av koden i disse pull requests. Det ingeniørene eier er arkitekturen, begrensningene og produksjonsoppførselen.

Hva er et test-trofe, og hvorfor ikke en testpyramide? Et test-trofe har statisk analyse i bunnen, integrasjonstester som dominerende lag og et lite antall ende-til-ende-tester på toppen. vm0 bruker dette fordi tester skrevet gjennom en moduls offentlige grense fortsetter å beskytte oppførsel mens en agent omorganiserer implementasjonen under, noe et stort enhetstestlag ikke gjør.

Hvordan finner dere død kode i et AI-skrevet repository? Kodegenerering legger til filer og abstraksjoner; sletting krever vanligvis en egen prompt. vm0 kjører Knip som en fast sjekk for å finne ubrukte filer, eksporteringer, avhengigheter og inngangspunkter. TypeScript bekrefter at koden er gyldig; Knip spør om den fortsatt deltar i systemet, som er det som betyr noe for død kode.

Er vibe-kodet kode trygg å kjøre i produksjon? Det avhenger av hva som verifiserer den, ikke hvem som har skrevet den. Signalene vi krever, har ikke endret seg: typer og kontrakter, tester gjennom offentlige grenser, ekte infrastruktur og en pipeline som må være grønn. Tallene i denne artikkelen beskriver repositoryets størrelse og leveringshastighet. Tilgjengelighet, feilrater og endringsfeilrater er målene som besvarer produksjonsspørsmålet, og vi samler dem fortsatt.

Åtte måneder inn

Åtte måneder er for tidlig til å erklære en endelig metode. Modeller, agentverktøy og repositoryet fortsetter å endre seg, og våre regler og arbeidsflyter endres med dem.

Ett skifte er allerede tydelig. Etter hvert som kodegenerering ble raskere, tok miljøet, begrensningene, testene og tilbakemeldingssystemet mer av kvalitetsbyrden. Ingeniører bruker mindre tid på å skrive implementasjon og mer tid på å definere oppførsel, designe grenser og forbedre verifikasjon.

vm0-kodebasen vil fortsette å vokse. Knip fjerner deterministisk restprodukt. Statiske regler blokkerer feil-mønstre vi allerede forstår. Integrasjonstester beskytter moduloppførsel. Gjentakende arbeidsflyter håndterer forringelse vi ennå ikke kan uttrykke maskinelt.

Kode betyr fortsatt noe. Beste praksis i denne artikkelen handler ikke om å skrive mindre kode, men om å avgjøre hva som må være sant før en endring hører hjemme på hovedgrenen. Vi bruker nå mer maskinutførbart bevis for å avgjøre om en endring hører hjemme på hovedgrenen, og om implementasjonen bør forbli i repositoryet.

Stay in the loop

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