Code met de draad mee: de skill die voorkomt dat AI-codingagents je repo opblazen

Gepubliceerd op Sep 22, 2026 door Štefan Moravík.
AI Agents Claude Code Developer Tools Code Quality
Op deze pagina 9 min lezen
  1. 1De korte versie
  2. 2De fout waar dit voor is
  3. 3Draad
  4. 4Paardenstaart
  5. 5Een agent het echt laten doen
  6. 6Wat er in de repo gebeurde, inclusief het deel dat niet werkte

De korte versie

  • AI-codingagents zijn goed in code toevoegen en slecht in opmerken dat de code al bestaat. Als je ze hun gang laat gaan, geven ze een repo een tweede helper voor elke eerste, en elke lezer moet beide controleren om één ervan te vertrouwen.
  • De oplossing zijn twee gewoontes die opgeschreven staan zodat een agent ze volgt. Draad: vind het dichtstbijzijnde bestaande voorbeeld van hetzelfde soort ding en spiegel de vorm, plek en namen. Paardenstaart: beklim een ladder van “moet dit bestaan” tot “het minimum dat werkt” en stop bij de eerste trede die houdt.
  • Beide zijn nu een publieke skill, MIT-licentie: grain-skill op GitHub . Hij bevat de schrijfregels, een reviewpass met vaste tags, en de vorm van de afsluitende regel die de agent laat benoemen wat hij heeft gespiegeld.
  • In onze hoofdrepo sinds augustus: 386 issues en pull requests dragen een plancomment dat het te spiegelen voorbeeld benoemt, over 1.333 commits in een boom van ongeveer 18.000 bestanden. 35 plekken in de code dragen een ponytail:-comment dat een bewuste vereenvoudiging en het upgradepad ervan benoemt.
  • De ongemakkelijke bevinding: de agent riep de skill nul keer als skill aan. De regels werkten omdat ik ze ook in de CLAUDE.md van het project zette, in de definities van de worker-agents en in de reviewchecklist. Zet de regel waar de agent al leest.

Ik heb hier eerder over geschreven en ik vond het resultaat niet goed, omdat het het principe beschreef en niet het mechanisme. Dit is de versie met het mechanisme.

De fout waar dit voor is

Drie voorbeelden uit onze backend, allemaal echt, allemaal uit door agents geschreven pull requests die voor hun tests slaagden.

Een nieuwe REST-route die de applicatieservice rechtstreeks uit een factory haalde. Elke zusterroute in de repo gaat van route naar controller naar service naar repository, elke stap geïnjecteerd. Deze klapte de keten in tot route naar service. Het werkte. Het betekende ook dat de enige plek waar requestvalidatie en responsevormgeving voor die feature leven, niet bestond, en dat de volgende agent die de feature aanraakte twee vormen had om uit te kiezen.

Een te grote service opgesplitst in _payloads.py, _protocol.py en _tokens.py, elk een module met kale async defs, waarbij de service protocol.discover(...) aanroept via import. De bestandslengtecheck werd groen. Precies het ding dat dependency injection moet voorkomen, kwam direct terug: een collaborator die op importtijd wordt gekozen, niet te vervangen zonder de module te patchen, afwezig in de constructor die documenteert waarvan de service afhankelijk is.

Een tweede normalizer. Er was er al één. De agent vond hem niet, schreef nog acht regels die bijna hetzelfde deden, en nu beweren twee functies met verschillend randgevalgedrag hetzelfde veld te canonicaliseren.

Geen van deze is een bug in de zin die een test vangt. Elk is een agent die kiest voor de code die hij kon genereren boven de code die bestond. Doe dat over een paar honderd pull requests en de repo heeft geen vorm meer.

FlowHunt Logo

Klaar om uw bedrijf te laten groeien?

Start vandaag uw gratis proefperiode en zie binnen enkele dagen resultaten.

Draad

Nieuwe code moet niet te onderscheiden zijn van de code eromheen. De skill verandert die zin in vijf stappen die een agent neemt voordat hij schrijft.

Vind eerst het voorbeeld. Voor een endpoint, service, repository, query hook, store slice of component zoek je de dichtstbijzijnde bestaande en kopieer je de vorm, bestandslocatie en naamgeving. Verzin geen structuur als er een patroon bestaat. In onze repo is de branding-slice het benoemde voorbeeld voor de backend, omdat hij klein, schoon en over alle lagen verdeeld is, en de tabel in de skill zegt voor elk soort artefact welk bestand je opent en wat je spiegelt.

Respecteer de lagen. Presentatie roept applicatie aan, applicatie roept domein aan, en infrastructuur implementeert het domein. Een nieuwe import over die lijnen heen zakt voor de architectuurcheck. Lees het lagen-document, leid het niet opnieuw af.

Lokale draad tegenover repo-draad. Match je broers en zussen. Maar als die broers en zussen zelf afwijken van de canonieke keten, volg dan de canonieke keten en noteer de afwijking in plaats van de drift te kopiëren. “Het matcht de andere vijf routes in dit bestand” is niet genoeg als alle zes een laag overslaan die de canonieke slice wel gebruikt. Je vergeleek te lokaal.

Hergebruik de canonieke helper. Als een helper, type of patroon al in de repo staat, gebruik die. Twee normalizers dwingen elke lezer beide te diffen.

Namen dragen betekenis. Hergebruik het vocabulaire dat al in de code staat. Een nieuwe naam voor een bestaand concept is een bug die op een lezer wacht.

De test aan het eind is de test die ik reviewers geef: als je alleen aan de vorm kunt zien welke regels de agent schreef, vocht hij tegen de draad in.

Paardenstaart

Lui betekent efficiënt, niet achteloos. De beste code is de code die nooit geschreven is. Begrijp eerst het probleem volledig, elk bestand dat de wijziging raakt, de echte end-to-end flow, en beklim dan deze ladder en stop bij de eerste trede die houdt.

De luiheidsladder: moet het bestaan, staat het al in de codebase, stdlib, native platform, geïnstalleerde dependency, één regel, en pas dan de minimale code die werkt

Twee regels liften mee met de ladder.

Een bugfix is een hoofdoorzaak, geen symptoom. Een ticket benoemt een symptoom. Grep elke aanroeper van de functie die je op het punt staat aan te raken. Eén guard in de gedeelde functie is een kleinere diff dan een guard in elke aanroeper, en alleen het pad patchen dat het ticket noemt, laat de zuster-aanroepers stuk achter.

Houd de diff minimaal. De diff is wat de review leest. Geen drive-by refactors, geen hernoemingen, geen herformattering, geen “nu ik hier toch ben”-verbeteringen, geen bestanden aanraken die de taak niet vereiste. Elk daarvan begraaft de echte wijziging. Als de diff groter is dan het probleem, snoei hem terug. Verbeteringen die je zag, zijn een vervolgissue, niet extra commits op deze branch.

En de grenzen, want “lui” is het woord dat het meest verkeerd gelezen wordt. Vereenvoudig nooit inputvalidatie op een vertrouwensgrens weg, foutafhandeling die dataverlies voorkomt, security, toegankelijkheidsbasisprincipes, of iets wat de gebruiker vroeg. Wees nooit lui in begrijpen: de ladder verkort de oplossing, nooit het lezen. Een kleine diff op de verkeerde plek is een tweede bug. Niet-triviale logica laat één uitvoerbare check achter, het kleinste ding dat faalt als de logica breekt.

Als de agent bewust een trede neemt en het plafond kent, zegt hij dat in de code:

# ponytail: global lock, per-account locks if throughput matters

Daar staan er nu 35 van in onze repo. Elk is een beslissing die een toekomstige lezer anders opnieuw zou moeten reconstrueren, en een marker voor waar je moet kijken als het plafond wordt geraakt.

Een agent het echt laten doen

Een principe in een document is geen gedrag. Drie mechanismen maakten deze twee tot gedrag, en ik zou ze in deze volgorde rangschikken.

De briefing draagt het voorbeeld. In het architectpatroon schrijft het hoofdmodel een briefing voor een worker, en het Design-veld van die briefing benoemt het dichtstbijzijnde bestaande patroon om te spiegelen en de minimale hoofdoorzaakwijziging. De worker hoeft de draad niet te vinden. Hij krijgt hem aangereikt. In onze repo is dat wat de 491 plancomments op 386 issues en pull requests zijn: een planningsstap die benoemt wat te spiegelen voordat iemand schrijft.

De agent sluit af met wat hij heeft gespiegeld. Elk implementatierapport eindigt met één regel, mirrored: <path>, of benoemt het nieuwe patroon en waarom geen enkel broertje paste. Dit is goedkoop en het is het beste reviewhulpmiddel dat ik heb toegevoegd. Een mirrored:-regel die naar het verkeerde soort bestand wijst, vertelt je dat de diff fout is voordat je hem opent. Een ontbrekende regel vertelt je dat de agent niet heeft gekeken.

De review handhaaft dezelfde lat, in een vaste volgorde. Onze reviewskill heeft twee passen met vaste tags, en die tags zijn wat bevindingen vergelijkbaar maakt tussen reviewers en tussen agents.

Twee reviewpassen: de structuurpass tagt diverge, duplicate, layer, misplaced, reach-in, inconsistent, own en chain; de schrappass tagt delete, stdlib, native, yagni en shrink. Structuur gaat eerst omdat consistentie wint van schrappen

Structuur eerst. Wijkt dit af van het dichtstbijzijnde voorbeeld, dupliceert het een bestaande helper, overschrijdt het een laag, staat het op de verkeerde plek, grijpt het in de interne werking van een andere module, gebruikt het inconsistente namen, neemt het eigendom van iets wat het zou moeten injecteren, of breekt het de canonieke aanroepketen? Dan schrappen. Wat kan verwijderd worden, vervangen door de standaardbibliotheek, vervangen door een native functie, geschrapt als speculatief, of verkleind?

De volgorde is een regel, geen voorkeur. Consistentie wint van schrappen. Een schrap die code achterlaat die afwijkt van zijn broers en zussen is geen winst, want de lezer moet nu twee vormen leren om acht regels te besparen. Laat structuur eerst lopen en de schrappass raakt alleen code aan die al matcht.

Wat er in de repo gebeurde, inclusief het deel dat niet werkte

Sinds 1 augustus heeft onze hoofdrepo 1.333 commits opgenomen, de meeste door agents geschreven onder review. 386 van de issues en pull requests in die periode dragen een plancomment dat het te spiegelen voorbeeld benoemt, in totaal 491 zulke comments. De canonieke keten is de standaardvorm van een nieuw endpoint, en de twee legacy-routes die de controller overslaan, staan in het document als een afwijking die wordt uitgefaseerd, niet als een tweede stijl.

De 35 ponytail:-comments zijn een kleiner aantal dan ik verwachtte en ik denk dat dat klopt. De meeste vereenvoudigingen zijn geen comment waard. Die het wel zijn, zijn degene met een plafond.

Nu het deel dat ik liever niet rapporteer. Ik leverde draad en paardenstaart als een Claude Code-skill: een beschrijving in de skill-index, een body die op aanvraag laadt, en een regel in CLAUDE.md die zegt hem aan te roepen voordat je code schrijft. Over vijf weken transcripts riep de agent hem nul keer bij naam aan. De afsluitende regel mirrored: verscheen in zeven responses.

De regels landden alsnog. Ze landden omdat dezelfde tekst ook op drie plekken stond die de agent leest zonder te kiezen: de CLAUDE.md van het project, die vanaf de eerste beurt in context zit; de definities van de worker-agents, die “spiegel de vorm, locatie en naamgeving van het dichtstbijzijnde bestaande voorbeeld” en “grijp naar de luiste oplossing die daadwerkelijk werkt” als regels stellen, niet als een skill om te overwegen; en de reviewchecklist, waarvan de tags een pull request laten zakken dat ze negeert. De body van de skill werd de canonieke referentie waarnaar die drie verwijzen. Als trigger deed hij niets.

De les die ik zou doorgeven aan iedereen die conventies voor een agent verpakt, is dus deze. Een skill is een goede plek voor de lange versie van een regel. Het is een slechte plek voor de regel zelf, want de agent moet beslissen hem te laden en dat zal hij meestal niet doen. Zet de versie van één alinea in het bestand dat altijd in context zit, zet de imperatieve versie in de definitie van de agent die de code schrijft, en zet de check in de review die de merge tegenhoudt. Verwijs dan alle drie naar de skill voor de details.

De publieke versie op github.com/LivinTribunal/grain-skill is zo opgezet: een korte SKILL.md met de regels die een agent in zijn hoofd kan houden, het volledige document met de ladder en de anti-patronen, de reviewtags, en een README die zegt welke alinea in je CLAUDE.md hoort en welke in de definitie van je implementer. Vervang het uitgewerkte voorbeeld door je eigen canonieke voorbeeld en je bent klaar.

Veelgestelde vragen

Štefan is an AI and software engineer building FlowHunt. Beyond the product itself, he designs agentic software-engineering workflows for developers that cut development costs while raising code quality.

Štefan Moravík
Štefan Moravík
AI & Software Engineer

Geef je agents de conventies die je team al volgt

Met FlowHunt koppel je de regels, voorbeelden en reviewstappen die een agent moet volgen aan elke workflow, zodat de output past bij de code waarin hij terechtkomt.