← Blog

Tooling

agents.md playbook: zeven secties die het verschil maken

Een dinsdag in Groningen: vier engineers openden Codex op dezelfde monorepo en pushten vier verschillende kapotte PRs. De fix zat niet in het model. Die zat in een ontbrekend bestand.

Jacob Molkenboer· Oprichter · A Brand New Company· 8 jun 2026· 9 min
Manilla map met getypte indexkaart, limegroen plaknotitie, messing paperclip, leren notitieboek, rode lakzegel op ivoor papier.

Een dinsdag in mei, bovenste verdieping boven een koffiebranderij aan de Oude Kijk in 't Jatstraat in Groningen. Vier engineers bij een SaaS van 41 mensen hadden die ochtend allemaal OpenAI's Codex geopend op dezelfde backend-monorepo. Tegen 16:00 hadden drie van hen PRs gepusht die de staging build sloopten. De vierde had de hele dag aan Codex gevraagd waarom een Drizzle-migratie steeds terugrolde. De senior met dienst klapte zijn laptop dicht en zei de zin waarmee deze opdracht begon: het kostte minder tijd om het met de hand te doen dan om de codebase elke keer opnieuw aan het model uit te leggen.

Drie dagen later leverden we een agents.md op. Twee dagen daarna mergeden diezelfde vier engineers Codex-geschreven PRs naar staging zonder oppas. De maatstaf van het team was bot: een verse Codex-sessie van "ik wil een webhook toevoegen" tot een groene CI-run duurde eerst grofweg drie dagen verwarrend heen-en-weer, en nu één middag geconcentreerd werk.

Dit is het bestand dat we schreven, en de zeven secties die het werk deden. Vijf andere secties die we probeerden zijn gesneuveld omdat ze niets opleverden, of het juist erger maakten.

Waarom de README het verkeerde bestand is

Een thread die deze week op de HN-voorpagina stond ging over de vraag of agents.md-bestanden coding agents écht helpen, en de reacties splitsten zich precies zoals je zou verwachten. De helft zei: dat is gewoon een README met een chique naam. De andere helft zei: het is het verschil tussen een agent die werkt en een agent die een Stripe-integratie hallucineert die niet in jouw codebase bestaat.

Allebei kloppen, en de reden zit in waar elk bestand voor geoptimaliseerd is. Een README is geschreven voor een mens die het één keer leest, scant, het team een vraag stelt en de rest leert door code te lezen. Een agents.md is geschreven voor een model dat het elke beurt opnieuw leest, geen team heeft om iets te vragen, en leert door code te lezen die te groot is voor zijn context window. Dat zijn verschillende klussen.

De agents.md-conventie die meerdere coding tools nu automatisch inladen (Codex, Aider, Sourcegraph Amp, onze eigen interne harness) laat je één bestand schrijven dat de agent zonder gedoe oppikt. Wat erin staat is aan jou. Na drie herschrijvingen is dit wat we hebben gehouden.

1. Runtime-randvoorwaarden, met de exacte pins

De eerste sectie is zes regels lang. Node 20.11.1. pnpm 9.4.0. Postgres 16. Redis 7. De pnpm-versie is van belang omdat lockfile v9 stilletjes opnieuw wordt gegenereerd onder v8, en het model "fixt" daarna de diff. Postgres 16 is van belang omdat de codebase generated columns gebruikt die 15 niet ondersteunt. Codex probeerde ze twee keer om te schrijven naar triggers voordat we de versie hadden vastgezet.

De truc zit in de tweede helft van de sectie: wat je nooit moet installeren. De repo heeft een halfafgemaakte Bun-migratie in een feature branch, en Codex pakte steeds de bunfig.toml op en ging ervan uit dat Bun de runtime was. Eén regel ("Deze repo draait op Node. Bun-configuratiebestanden zijn restanten van een experiment uit 2025. Voer geen bun install uit.") sneed die loop er volledig uit.

2. De twee commando's die altijd werken

Codex probeert elk commando in het scripts-blok van package.json. De meeste werken niet in isolatie, of blijven hangen omdat ze op een TTY wachten. De fix is om de twee of drie commando's op te sommen die de agent mag gebruiken, en de rest expliciet te verbieden.

# Running the project

You can rely on these:
- `pnpm dev`        : starts the API on :4000, hot reload
- `pnpm test:unit`  : runs in ~12s, no DB needed
- `pnpm test:int`   : runs against a Docker Postgres, ~90s

Avoid:
- `pnpm start`      : production build, expects secrets from Doppler
- `pnpm test`       : runs e2e, needs a browser, hangs in CI mode
- `pnpm db:reset`   : wipes the local DB without confirmation

Die laatste regel heeft in de eerste week twee keer een ochtend van een engineer gered.

3. De repo-kaart, met redenen

Een normale README-repo-kaart zegt "src/billing doet de facturatie." Dat is niet genoeg. De agent moet weten welke map welk onderwerp bezit, en welke mappen er verwant uitzien maar het niet zijn. We hebben de kaart daarom als een lijst regels geschreven in plaats van als een tree.

# Where things live

- `src/billing/`           : Stripe webhooks live ONLY here.
                              If you find yourself adding a
                              second webhook handler somewhere
                              else, stop and ask.
- `src/inbox/`             : the multi-tenant inbox. Never
                              import from `src/legacy-inbox/`.
                              That folder is on a deletion
                              schedule.
- `src/jobs/`              : BullMQ workers. One file per
                              queue. New jobs need a row in
                              `config/queues.ts`.
- `packages/db/`           : Drizzle schema. The only place
                              schema lives.
- `packages/contracts/`    : shared Zod schemas. If a type is
                              used by both API and worker, it
                              goes here.

De "never import from"-regel was het belangrijkst. De codebase heeft twee inbox-implementaties omdat een migratie was gepauzeerd. Zonder die regel bedraadde Codex met plezier features in de dode versie, want die had meer code comments.

4. De conventies die stilletjes door CI heen breken

Dit is de sectie die we steeds opnieuw hebben geschreven. De agent breekt de voor de hand liggende regels niet. Hij breekt de regels waar de lint-regel in een config-bestand drie mappen diep zit en de foutmelding "fixable with --fix" zegt, waarna Codex aanneemt dat het niet belangrijk is.

Drie regels gingen erin:

  • Import order wordt afgedwongen door eslint-plugin-import. Draai altijd pnpm lint --fix voordat je committed.
  • Geen relatieve imports tussen packages. Gebruik de @app/*-aliassen. De default van Codex is ../../../ en dat zeilt door TypeScript heen om vervolgens te falen op runtime.
  • Tests draaien op Vitest, niet op Jest. Voeg geen Jest-types toe. De twee hebben overlappende API's en de LSP autocompletet de verkeerde.
Waarschuwing

Als je deze sectie schrijft als "volg de bestaande stijl," heb je niets geschreven. Dat doet de agent al, en het gaat ongeveer een derde van de tijd mis omdat de bestaande stijl niet consistent is. Benoem de regel, benoem de tool, benoem de failure mode.

5. Het domeinwoordenboek

Elke B2B SaaS heeft drie woorden die in verschillende bestanden iets verschillends betekenen. Bij dit bedrijf waren dat "workspace," "tenant" en "policy." Een workspace is de logische container van een klant. Een tenant is een row-level-security-grens in Postgres, die meestal maar niet altijd één op één met een workspace overlapt. Een policy is óf een billing policy (in src/billing) óf een authorization policy (in packages/auth), en ze delen een type-naam.

Vóór het woordenboek genereerde Codex een functie getPolicyForTenant die de twee concepten combineerde en de verkeerde service aanriep. Na het woordenboek vroeg hij welke we bedoelden. Dat is het hele spel.

De sectie is vier alinea's. We hebben niet geprobeerd uitputtend te zijn. We benoemden de woorden die overloaden, gaven de canonieke definitie en noemden het bestand waar het canonieke type leeft.

6. Het migratieritueel

Databasemigraties zijn de plek waar agents de duurste fouten maken, dus deze sectie is de langste in het bestand. Het is ook het meest regel-vormige stuk, dus dit deel van de doc leest als een vluchtchecklist.

# Migrations

We use Drizzle Kit. The process is:

1. Edit the schema in `packages/db/schema/*.ts`.
2. Run `pnpm db:generate`. This creates a new SQL file
   in `packages/db/migrations/`.
3. Read the generated SQL. If it includes `DROP COLUMN` on
   a column that is not behind a feature flag, STOP and ask.
4. Run `pnpm db:migrate` to apply locally.
5. Commit BOTH the schema change AND the generated SQL
   file in the same commit.

Hard rules:
- Never edit a migration file that has already shipped to
  staging. Write a new one.
- Never use `db:push`. It bypasses the migration history
  and we cannot replay it on production.
- Backfills go in a separate migration from the schema
  change. Schema first, deploy, then backfill.

De regel "nooit een migratiebestand bewerken dat al naar staging is" was degene die Codex het vaakst brak voordat we hem opschreven. Het instinct van het model is om het bestand met de bug te fixen. De juiste move is een nieuw bestand schrijven dat de oude ongedaan maakt. Dat onderscheid is niet duidelijk als je de codebase leest, want de migratiebestanden zien er allemaal hetzelfde uit.

7. De definitie van klaar

De laatste sectie is een checklist die de agent doorloopt voordat hij claimt dat een taak af is. Hij bestaat omdat Codex standaard "ik heb de endpoint toegevoegd" zegt op het moment dat het bestand compileert. Compileren is niet klaar.

# Before you say a task is done

Run these in order. If any fail, the task is not done.

1. `pnpm lint` : zero warnings, not just zero errors.
2. `pnpm typecheck` : must pass with no `// @ts-expect-error`
   added.
3. `pnpm test:unit` : all green.
4. If the task touched `packages/db/`, run `pnpm test:int`
   too.
5. Open the diff. If it includes a new dependency in
   package.json, justify it in the PR body.

Die laatste regel sneed een klasse van PRs eruit waarin de agent een date-library van 40KB had binnengehaald om één ISO-string te formatteren.

Wat we eruit hebben gesneden

De eerste versie van het bestand was 900 regels. We hebben vijf secties geschrapt, omdat de agent ze óf negeerde óf er slechter van werd als ze in zijn context stonden.

We schrapten het architectuuroverzicht. Drie alinea's over "event-driven, eventually consistent, CQRS-influenced" zorgden ervoor dat het model een feature ging over-engineeren die alleen maar een database-rij nodig had.

We schrapten de contactlijst van het team. De agent heeft niemand om een bericht te sturen.

We schrapten een "common pitfalls"-sectie die een kerkhof van oude incidenten was. Geen van die incidenten was relevant voor de volgende taak en ze verdrongen de regels die er wél toe deden.

We schrapten een sectie "design principles." Daardoor ging het model essays schrijven in PR-beschrijvingen in plaats van code.

En we schrapten een lijst met voorkeursbibliotheken. Codex koos de juiste al, omdat ze in de repo stonden. Door ze nogmaals te noemen ging hij twijfelen.

Kernpunt

Schrijf de agents.md voor de failure modes van de agent, niet voor een nieuwe collega. Elke regel staat er omdat je zonder die regel het model iets fout zag doen.

Voorkomen dat het verrot

Een agents.md die niemand bijwerkt is erger dan helemaal geen, want het model gaat erop vertrouwen. We hebben één regel toegevoegd aan het engineering handbook: elke PR die een nieuwe regel toevoegt die de agent moet weten, voegt in dezelfde commit ook een regel toe aan agents.md. Het bestand groeide in de eerste maand met 14 regels. Geen daarvan ging over architectuur. Allemaal waren ze "het model probeerde X, dit is waarom X fout is."

De andere nuttige gewoonte is om het bestand één keer per kwartaal hardop voor te lezen. Wat niet meer klopt wordt verwijderd. Wat vaag is wordt benoemd. Wat je niet kunt verantwoorden wordt geschrapt. Het bestand is op dit moment 312 regels, en het team vindt dat het ongeveer honderd regels te lang is. Ze hebben waarschijnlijk gelijk.

De versie van vijf minuten

Wil je vandaag beginnen: open een nieuw bestand agents.md in de root van je repo. Schrijf alleen sectie één, twee en zeven (randvoorwaarden, twee commando's die werken, definitie van klaar). Lever dat op. Geef het een week. Voeg dan een woordenboek-sectie toe zodra de agent twee van je domeinwoorden door elkaar haalt. Dat is het hele proces. De versie van zeven secties is wat je krijgt na een maand opschrijven wat er fout ging.

Toen we de agents.md bouwden voor het team in Groningen, liepen we er steeds tegenaan dat Codex de pgvector-helper aanriep vanuit de API-laag in plaats van vanuit de worker, waar hij thuishoort. We hebben dat niet in de code gefixt. We hebben het opgelost met twee regels in sectie drie van het bestand. Loop je tegen dezelfde muur op met je coding agents: dat is precies het werk dat we doen als consultants rond AI-agents. Korte trajecten, het bestand woont in jouw repo, jouw team beheert het daarna.

Kern

Schrijf agents.md voor de failure modes van het model, niet voor een nieuwe collega. Elke regel verdient zijn plek door iets te fixen wat je de agent fout zag doen.

FAQ

Wat is een agents.md-bestand?

Een tekstbestand in de root van een repo dat AI coding agents automatisch inlezen. Het vertelt de agent hoe de codebase draait, wat te vermijden is, en wanneer een taak af is.

Hoe verschilt het van een README?

Een README is voor een mens die één keer scant en het team iets vraagt. Een agents.md is voor een model dat hem elke beurt opnieuw inlaadt, niemand kan vragen, en expliciete regels nodig heeft in plaats van sfeer.

Welke AI coding tools lezen agents.md?

Codex, Aider, Sourcegraph Amp en meerdere interne harnesses laden hem automatisch. Andere tools kun je er handmatig naar laten wijzen als onderdeel van hun system prompt.

Hoe lang moet een agents.md zijn?

Lang genoeg om de failure modes te dekken die je echt ziet, kort genoeg om door het team waar te houden. De onze landen meestal tussen 200 en 400 regels.

Welke sectie heeft de meeste impact?

De definitie van klaar. Die stopt de agent ermee te claimen dat een taak af is op het moment dat het bestand compileert, en dat is veruit de grootste bron van slechte PRs.

toolingai agentsworkflowoperationscase studyarchitecture

Iets bouwen?

Start een project