Naar zelfrijdende codebases

We zijn voorbij het 'tokenmaxxing'. Waar zijn we nu naar op weg? En hoe komen we daar?

Agents kunnen inmiddels games maken die daadwerkelijk leuk zijn. Met de juiste vangrails kunnen agents ongelooflijk indrukwekkende migraties uitvoeren in complexe codebases, en zelfs complete herschrijvingen in nieuwe talen doen. Toch wordt bijna al het "echte" softwarewerk nog steeds aangestuurd door menselijke engineers. Hoe komen we op een punt waar een veel groter deel van het werk voor ons wordt afgehandeld, zonder dat onze constante aandacht vereist is?

Het lijkt erop dat agents in staat zouden moeten zijn om volledige softwaresystemen zelf te bouwen. Waarom gaat dit in de praktijk zo moeizaam? Welke nieuwe bouwstenen (primitives) hebben we nodig om dit te laten werken? En hoe ver kan dit gaan?

Wat komt er na 'tokenmaxxing'?

Veel engineering-organisaties hebben het eerste halfjaar van dit jaar besteed aan het uitbesteden van zoveel mogelijk werk aan legers van agents en adversarial loops. De resultaten waren behoorlijk teleurstellend: bergen twijfelachtige code, maar geen vloedgolf van ongelooflijke software. De ROI op al die tokens was op zijn best twijfelachtig.

In termen van de hype-cyclus bevinden we ons in het 'dal van desillusie'. We zijn aan het uitzoeken hoe we de utopische visie die we een paar maanden geleden hadden — en die in de praktijk nog niet werkt — kunnen realiseren.

Wat gebeurt er nu? In elke andere adoptiecyclus is het antwoord: zoek uit wat de best practices zijn, bepaal waar onze nieuwe glimmende hamer echt geschikt is voor een spijker (en waar we hem hebben gebruikt om schroeven kapot te slaan), en creëer vervolgens de bouwstenen die we niet realiseerden dat we nodig hadden totdat we het nieuwe speelgoed in handen kregen.

Een diagnostische vraag die we kunnen stellen om ons doel te bereiken is: wat zullen engineers doen wanneer de software zichzelf grotendeels aanstuurt?

Een veelgehoord antwoord is: "Loops opzetten!". We schreven vroeger code, daarna schreven we prompts, en nu richten we loops van agents in en typen we vaak /goal.

Ik denk dat dit een misvatting is. Op dit moment is het opzetten van een levensvatbare software-loop die netto positieve softwarewijzigingen produceert zonder fortuinen aan geld te verbranden, enorm veel werk. Maar dat komt vooral omdat de toolchain er nog niet klaar voor is. Wanneer we de juiste onderdelen hebben, zullen deze loops eenvoudig op te zetten zijn, betrouwbaar zijn en kosteneffectief zijn. Hier zal onze tijd niet in gaan zitten.

In plaats daarvan zal het meest waardevolle engineering-werk liggen in het hebben van goede ideeën. De belangrijke ideeën komen nog steeds van buiten de 'softwarefabriek'. Sterker nog, het doel van deze hele machine is om het aantal zaken waar we over na moeten denken te minimaliseren, zodat we ons kunnen richten op zaken met een hoge potentie.

  • Killer features bepalen nog steeds het succes van een bedrijf. Cursor Tab, de transcript-bewerking van Descript, OpenRouter die inference-leveranciers abstraheert achter een gemeenschappelijke facturatie-interface; dit zijn allemaal bedrijf-makende ideeën. Naast creativiteit vereisen goede ideeën een intieme kennis van het probleem en domeinexpertise, en engineers zullen moeten bijdragen aan dat proces. [1]
  • Killer architecture biedt nog steeds een enorme hefboom. De juiste vereenvoudiging bespaart later een enorme hoeveelheid complexiteit, wat zich vertaalt in minder bugs en een makkelijkere productiteratie. Een beetje vooruitdenken maakt hoogwaardige functies later eenvoudig te implementeren. Er zit nog steeds veel waarde in het uitwerken van een correcter datamodel, het toevoegen van een queue op de juiste plek of het bouwen van de fundering voor een betere manier om het product te testen.

Welke delen zouden zelfrijdend moeten zijn?

Buiten het domein van "goede ideeën hebben", zouden we codebase-werk naar GPU's moeten verplaatsen. Kunnen agents bijvoorbeeld de volgende zaken volledig afhandelen?

  • Het detecteren en oplossen van de meeste bugs. Een nieuwe functie werkt niet correct als een gebruiker is ingelogd via SSO. De bulk-delete-functie update de rijteller bovenaan de pagina niet op dezelfde manier als de individuele delete-functie. Een agent zou dit voor ons moeten signaleren en oplossen. De meeste bugs hebben duidelijke, beoogde semantiek — we hebben geen menselijke engineer nodig om te beslissen hoe de software zou moeten werken, en we hebben misschien niet eens documentatie of een specificatie nodig.
  • Het debuggen van productiefouten. Wanneer er een fout optreedt, zou een agent deze moeten correleren aan een recente commit, een wijziging in het verkeer, een verschil in infrastructuur of een onverwachte conditie, en deze meestal direct voor ons patchen. Backends zouden in principe nooit meer 500-fouten moeten geven. Browser-consolelogs zouden vrijwel geen fouten meer mogen bevatten.
  • Het optimaliseren van agents. In hoeverre zouden we zelf agent-prompts moeten schrijven? Kunnen tools als Braintrust, Raindrop, Arize of Langfuse veelvoorkomende agent-pathologieën isoleren, updates voorstellen, deze backtesten en vervolgens implementeren?
  • Consistentie van de frontend. De visuele taal van elke app moet intern coherent zijn. Apps moeten consistente lettertypen, kleuren, iconografie en spatiëring gebruiken. Het designsystem zou zichzelf moeten opstarten en afdwingen, waarbij mensen alleen bevestigende beslissingen nemen.
  • Application polish. Apps zouden zich moeten gedragen zoals gebruikers verwachten. Ctrl-klik zou een link in een nieuw tabblad moeten openen. Uitgeschakelde knoppen zouden een uitleg moeten hebben waarom ze dat zijn. Formuliervelden zouden bewaard moeten blijven na een refresh. UI's zouden op mobiele apparaten redelijk moeten renderen en navigeerbaar moeten zijn via schermlezers. Er zijn tientallen van dit soort basisverwachtingen aan de gebruikskwaliteit.
  • Growth-optimalisatie. Itereren op formulierconversie, het optimaliseren van onboarding en het produceren van marketingpagina's. Er zijn playbooks met experimenten die goed werken, en agents zouden deze playbooks voor ons moeten uitvoeren. [2] Dit betekent niet dat 'growth' dood is — het is levendiger dan ooit! Maar het werk zal nu meer lijken op strategische operaties en minder op het optimaliseren van CTA's. Growth-teams stellen doelen vast, en agents itereren niet alleen op de code, maar tegen de realiteit door wijzigingen aan gebruikers voor te leggen en daarvan te leren. Hierdoor kunnen we ons richten op ideeën op een hoger niveau.

De lijst is lang. GitHub Copilot gaf ons autocomplete voor regels code; agents zullen ons autocomplete geven voor volledige producten.

Ontbrekende bouwstenen (Primitives)

Als dit moet gaan werken, heeft de dev-stack nieuwe bouwstenen nodig. Deze zullen essentieel zijn:

  • Agent-leesbare ontwikkelomgevingen. Bugs ontstaan overwegend op plekken waar agents ze niet kunnen zien. Als je app third-party integraties heeft die je agents niet end-to-end kunnen testen, zullen die integraties bugs bevatten. Als je repo geen goede agent-browser setup heeft, is de hele frontend een grijze zone waar je agents blind vliegen.
  • Globaal geheugen. Het is acceptabel als agents domme fouten maken. Wat niet acceptabel is, is dat ze dezelfde fouten blijven maken. Het is prima als je een agent moet vertellen dat de audit-log tabel append-only is en nooit in-place mag worden bijgewerkt, maar als je vervolgens elke agent die dat gebied aanraakt hetzelfde moet vertellen, zit je weer vast in low-level werk. Welk geheugensysteem we ook creëren, het moet werken over de hele toolchain — de code-review bot moet op de hoogte zijn van de correctie die je twee weken geleden gaf aan de agent die de code in dat gebied schreef. Wanneer je een SRE-bot terechtberispte voor een onveilige productie-operatie, moet de agent begrijpen waarom, en elke toekomstige agent moet op de hoogte zijn van deze correctie.
  • Globaal geheugen is grotendeels een kwestie van wachten op betere tools. Of we creëren een open standaard, of we gebruiken geconsolideerde suites die agents voor dagelijks gebruik, reviewers en alles wat we nodig hebben in één pakket aanbieden, of beide.
  • Preventie van codebase-rot. Laten agents dode code achter? Zijn er vijf manieren om iets te doen wat op één manier zou moeten gebeuren? Is het typesysteem intuïtief? Is het datamodel in lijn met het product dat we willen leveren? Iemand of iets moet dit beheren, anders zal de codebase in een spiraal naar beneden gaan.

We hebben daarnaast nog steeds de meeste bouwstenen uit het vorige tijdperk nodig, zoals APM (maar dit zal vooral door agents worden bevraagd via logs, in plaats van via UI-widgets), CI (maar met betere merge-queues) en A/B-testen (maar bedienbaar door agents).

Hoe bouw je een agent-leesbare ontwikkelstack?

Waar engineering-teams nu veel werk in moeten steken, is het creëren van echt goede cloud-ontwikkelomgevingen. Deze moeten het voor agents gemakkelijk maken om de code op alle nodige manieren te testen voordat men überhaupt kan vaststellen of er bugs in zitten. Op dit moment is de beperkende factor voor dev-agents de omgeving waarin ze opereren, niet de modellen of de harnesses zelf.

Anders gezegd: de agents zijn goed genoeg om veel meer werk te doen, mits ze opereren in omgevingen die goed genoeg zijn. Maar ze creëren bugs op plekken waar ze niet kunnen kijken: race-conditions die agents geen systematische manier hebben om te reproduceren, of trage queries omdat ze onwetend zijn over de vorm van de data. [3]

Waar agents blinde vlekken hebben, maken ze fouten. Dit beperkt het vertrouwen, wat betekent dat werk met meer strengheid moet worden beoordeeld, wat weer beperkt hoeveel we kunnen delegeren.

De belangrijkste investering die een engineering-team nu kan doen, is het extreem agent-vriendelijk maken van hun ontwikkelomgeving. Maar momenteel is dat een bodemloze put van monotoon werk en eenmalige oplossingen (schlep), zonder manier om te kwantificeren hoe je ervoor staat. Het vereist goede ideeën en gefocuste engineering om deze gaten in de ontwikkelomgeving te dichten. Een lokale dev-setup met representatieve testdata is een bewegend doel. De juiste nieuwe abstracties om het schrijven van goede tests eenvoudig te maken, zijn lastig te bedenken. Dit werk is uitdagend!

Dit werk is bovendien codebase-specifiek en is nog steeds een kunst die weinigen goed beheersen. Dat is een van de redenen waarom zoveel softwarefabrieken teleurstellen. We hebben veel betere manieren nodig om deze investering te doen.

Dat betekent dat er veel winst te behalen is bij het helpen van teams om hun dev-toolchains 'agent-ready' te maken. Hoe kan een engineering-team weten wat de laaghangende vruchten zijn? Hoe moeten ze het werk prioriteren om hun fabriek beter te laten werken? Hoe weten ze of ze het goed doen? Als we dit kunnen veranderen van een kunst in een wetenschap, en engineering-teams kunnen helpen methodische progressie te boeken, zullen de fabrieken gaan draaien.

Dit is onze beste schatting van de weg voorwaarts:

  1. Scan codebases op bugs. Word heel goed in het selecteren van bugs die er echt toe doen.
  2. Los de bugs op. Houd bij welke gaten het lastig maken om te valideren of fixes correct zijn, of dat bugs überhaupt echt zijn.
  3. Gebruik de traces van het oplossen van deze bugs om het werk te prioriteren dat nodig is om een codebase toegankelijker te maken voor agents.

Anders gezegd: het geheime plan van Detail is om een product te creëren dat zijn eigen werk bootstrapt, en dat werk vervolgens te gebruiken om engineering-teams te helpen weten welke investeringen ze moeten doen en hoe goed deze werken.

Als we een corpus van nuttig werk kunnen bootstrappen voor elke codebase — in dit geval bugs die het engineering-team opgelost zou willen hebben — kunnen we dat gebruiken om de meest renderende manieren te bepalen om elke codebase beter te maken voor agents. We kunnen benchmarken hoe 'agent-ready' een repo is. Engineers kunnen zich vervolgens storten op de gaten in hun ontwikkelomgevingen die er echt toe doen, totdat bijna alle low-level bugs autonoom kunnen worden afgehandeld. Dan kunnen engineers zich richten op het echte hoogwaardige werk: goede ideeën voor features bedenken en abstracties uitwerken die de implementatie vereenvoudigen.

Zo bereiken we het 'plateau van productiviteit' en zo zullen de beste engineering-teams de komende zes maanden opereren. Niet door 'tokenmaxxing', en zeker niet door AI te verbieden, maar door een proces als dit te gebruiken om de agent-gereedheid te verhogen en stapsgewijs steeds meer werk over te dragen.

We lanceren vandaag een onderdeel hiervan, maar je zult zien dat deze ideeën vorm blijven krijgen in ons product terwijl we bouwen aan deze visie.

Dit is niet nieuw

Technologie vordert door lager niveau-zaken over te dragen aan systemen die ze voor ons beheren.

In de jaren 2010 verplaatsten we onze software naar de cloud. Nu hoeven we niet meer na te denken over waar het fysiek draait, of zelfs op welke virtuele instantie elke service draait. Een kleiner, gefocust engineering-team kan nu een succesvol softwareproduct bouwen. We kunnen leven in het logische vlak, terwijl platforms de rest afhandelen. Maar we zijn door een soortgelijke transitie gegaan die in het begin erg rommelig was, en we moesten onderweg veel ontbrekende bouwstenen uitvinden.

Het opzetten van CI en CD was vroeger een project van meerdere maanden, maar nu we de juiste bouwstenen en volwassen tools hebben, is het een middagwerk (tenzij je een mainframe van een bank migreert). Een analoge transitie zal plaatsvinden met 'loops'.

***

Voetnoten

[1] Als "smaak" ooit een differentiator was, is dat nu niet meer. Maar goede ideeën zijn dat wel.

[2] Sommige app-domeinen zijn zeer goed begrepen. Voor een e-commerce website wil je e-mails bij het verlaten van het winkelmandje, "N andere gebruikers bekijken dit nu!"-widgets, een ankerprijs met een streep erdoor naast een lagere prijs, enzovoort. Agents zouden dit playbook voor ons moeten uitvoeren.

[3] Agents creëren ook enorme hoeveelheden onnodig defensieve code, uit het verlangen om zich te beschermen tegen elke denkbare ambiguïteit zonder te weten welke risico's realistisch zijn.