Agenter bygger også spaghettikode — og sætter sig fast i den
Artikel

Agenter bygger også spaghettikode — og sætter sig fast i den

AI-kodeagenter er gode til at bygge nyt og overraskende hurtige til at gøre kodebasen ubrugelig for sig selv. At fange det tidligt kræver målinger, der ikke ændrer sig mellem hver kørsel.

For et stykke tid siden var jeg på et projekt, der byggede en .NET- og React-platform i højt tempo. Hurtig prototyping, faseskift med få ugers mellemrum, oprydning udskudt fordi den næste idé altid var vigtigere. AI-agenter skrev det meste af koden, og det var den eneste måde vi kunne følge med på.

Det fungerede smukt et stykke tid. Så gjorde det ikke.

UI-ændringer begyndte at lande i de forkerte filer. Backenden gled ud af trit med det, frontenden gik ud fra. Agenterne havde bygget noget hurtigt, og de samme agenter var nu forvirrede over det, de havde bygget. At bede om en lille ændring betød i stigende grad, at man først skulle fikse tre andre ting.

Vilkårene var ikke venlige til at begynde med — det er hurtig prototyping sjældent, og de fleste agentprojekter falder ikke fra hinanden så synligt. Men det ekstreme tilfælde er bare en mere højlydt udgave af det stille. Selv beskeden kompleksitet sænker tempoet for en agent. Det sænker også tempoet for mennesker. Kompleks kode er sværere at arbejde i for alle. Find og fiks hotspots tidligt, så bevæger alle sig hurtigere gennem koden.

Observationen har dog fulgt mig, for det her er ikke længere kun et legacy-problem.

Spaghetti er ikke længere kun en legacy-historie

Når folk taler om, hvorfor AI-agenter har det svært med en kodebase, tyer de til de sædvanlige mistænkte: gamle systemer, mangelfuld dokumentation, kompleks spaghettikode. Alt sammen sandt. Alt sammen også problemer, vi har haft længe før agenterne kom.

Det nye er, at AI-byggede kodebaser glider ind i den samme tilstand. Nogle gange inden for uger. Duplikeret logik spredt ud over siloer, fordi ingen — menneske eller agent — huskede det andet sted, den boede. Funktioner der voksede fra tyve linjer til to hundrede, uden at nogen besluttede at lade det ske. Død kode der hober sig op fra iterative omskrivninger, tre versioner af det samme i træet fordi agenten gik videre, og ingen fjernede de gamle. Testdækning der startede tyndt og blev tyndere.

Den agent, der hjalp med at bygge rodet, har ingen særlig fordel, når den kommer tilbage for at rydde op.

Agenthygiejne hjælper

Meget af det her kan forebygges. Tydelige agentinstruktioner, velafgrænsede skills, gode CLAUDE.md-filer, gennemtænkte arkitektoniske guardrails — en erfaren operatør med det rette setup leverer mærkbart mindre kaos end en, der bare peger Claude mod et repo og håber. Det betyder noget, og jeg vil hellere starte der end nogen andre steder.

Men selv god praksis fjerner ikke driften helt. Og den giver ikke en måde at tjekke, om guardrails faktisk holder, ud over at åbne hver fil og læse. Håb er ikke en målestrategi.

Vi skriver ikke koden længere

Der er en mere stille version af problemet, som jeg synes er vigtigere end den tekniske.

Da man selv skrev koden, stolede man på den, fordi man huskede at skrive den. Man vidste, hvilke dele der bar lasten, fordi man havde lagt dem der. Den tillid kom af at have været inde i arbejdet.

Vi skriver ikke det meste af koden længere. Det gør agenterne. Når jeg som erfaren udvikler gennemgår det, en agent har produceret, kan jeg sige om formen ser rigtig ud — men jeg kan ikke ud fra læsningen alene sige, om duplikeringen nu er et problem, om én funktions cyclomatic complexity stille er blevet en hotspot, om navngivningen er konsistent med, hvordan resten af kodebasen har udviklet sig over de sidste hundrede commits.

Læsningen alene er ikke nok. Man har brug for et målelag, der ikke var en del af det, man kigger på.

Deterministisk og probabilistisk, ikke det ene eller det andet

Branchens refleks er at smide en LLM efter problemet. “Lad bare Claude bedømme kodebasen.” Det virker, delvist. LLM’er er gode til vurdering, til at lægge mærke til når noget føles forkert.

Men stil den samme LLM det samme spørgsmål tirsdag og torsdag, og man får to forskellige svar. Nyttigt til triage, ubrugeligt som en baseline man kan følge over tid.

Det man faktisk har brug for er begge dele. Deterministisk analyse for grunddata — AST-parsing returnerer det samme tal for det samme repo, hver gang, uanset hvem der spørger. Probabilistisk analyse for vurdering oven på de tal, hvor LLM’er er stærkest. Dikotomien “regelbaserede værktøjer mod LLM’er” er falsk. Produktionssystemer har brug for begge, hver for det de er gode til.

Hvad målbart faktisk betyder

Det her er ikke luftigt. De interessante målepunkter for AI-parathed reducerer til tal, der er reproducerbare og lette at forsvare.

Median cyclomatic complexity under 5: sundt. Over 30: agenten får det skidt. Kodeduplikering over 3% er der, hvor man begynder at fikse fejlen ét sted og overse den tre andre steder. Funktioner med en gennemsnitlig længde under 20 linjer passer rent ind i kontekst; gennemsnit over 200 gør de ikke. Type annotation coverage over 90% gør det, agenten gætter på, til noget den ved. Et test-kode-forhold under 0,05 betyder, at der i praksis ikke er nogen feedback-loop, og at man ikke kan stole på, at en agent verificerer sit eget arbejde uden en.

Ingen af de tal er tilfældige. De er de knapper der, drejet den forkerte vej, forudsigeligt forringer det, en agent kan udrette.

Sikkerhed er den samme historie med en højere pris

Agenter gengiver det, de ser. Hvis kodebasen indeholder hardcodede secrets, SQL injection-mønstre eller sårbare dependencies, vil agenten muntert gengive flere af dem. Sikkerhedsniveauet i kodebasen bliver sikkerhedsniveauet for det, agenten skriver.

Der er en særlig hage ved dependencies, som ingen taler nok om. LLM’er er trænet på årevis af offentlig kode, inklusiv library-versioner der siden har fået CVE’er imod sig. Modellen ved ikke, hvad den ikke ved — den foreslår en pakkeversion, der var aktuel i 2023, og kigger en lige i øjnene imens. Et deterministisk CVE-tjek mod en aktuel vulnerability database fanger det. LLM’en kan ikke.

Den samme dynamik gælder for SBOM-generering, som i stigende grad er påkrævet under EU’s Cyber Resilience Act for software, der sælges på EU-markedet. Det er et parser-job, ikke et LLM-job.

Hvad vi gør med det

Vi har bygget vores eget værktøj til netop dette og kører det på hver kundekodebase, vi arbejder på. Det producerer en parathedscore på tværs af strukturel kompleksitet, organisation, dokumentation, test, sikkerhed og hvor rent koden skæres op i AI-kontekstvinduer. Ofte er det det første vi gør på en ny opgave — det giver os og kunden et fælles kort, før vi rører ved noget som helst. Et separat indlæg om produktet er på vej.

Princippet er den del, der ikke afhænger af hvilket værktøj man bruger. Hvis man lader agenter arbejde på en kodebase, man ikke har målt, så håber man at de gør det godt — man styrer ikke, om de gør det. Tillid kom førhen af at skrive koden selv. Nu er den nødt til at komme et andet sted fra.

Værd at vide hvorfra.