Le budget de 1 500 mots qui empêche tes agents de saccager ton repo
JDJean Desauw
14 min de lecture
Lead React Native freelance
Lead React Native, en régie.
Deux ans de lead sur Odisei Play : une seule codebase sur iOS, Android et web, une release toutes les deux semaines, à deux engineers avec des agents IA. Ce rôle dans votre équipe, ou le setup du repo qui rend ce rythme possible.
Ce que je fais vraiment quand je prépare une codebase au coding agentique, et pourquoi « ajoute un AGENTS.md et shippe » n'en fait pas partie.
Le désordre que tes agents câblent tranquillement dans le repo
Ta codebase se salit un peu plus à chaque fois qu'un agent tourne, et tu peux ne rien voir venir jusqu'au jour où une designeuse demande pourquoi il existe quinze composants bouton. Des fichiers de règles qui grossissent jusqu'à ce que plus personne ne les lise. Des agents qui ont l'air occupés et qui laissent un désordre que personne ne peut démêler.
Le danger, ce n'est pas que les équipes ne voient pas la valeur. C'est qu'elles pensent aller vite alors qu'elles câblent tranquillement du chaos dans le code dont leur équipe va hériter.
Un repo que tes agents peuvent lire, et une équipe qui garde le contrôle. C'est tout le but.
Commence par ce que l'agent voit, pas par le modèle
Arrête d'accuser le modèle. Le modèle va bien. Le problème, c'est le repo que tu lui donnes.
L'agent n'est jamais meilleur que le contexte qu'on lui donne. Un repo où les conventions sont implicites, où la structure est « évidente pour les humains », et où les règles vivent dans la tête de quelqu'un, c'est un repo où l'agent va deviner. Et quand il devine, il génère des issues de suivi plus vite qu'on ne peut les faire passer en review.
Alors je commence par rendre la codebase lisible exprès. Les conventions écrites. Une structure découvrable, pas tribale. Cette clarté aide aussi tes ingénieurs humains. Un repo que tes agents peuvent lire, c'est un repo que ton équipe peut lire.
C'est le travail que je fais dans les repos clients : le routeur, la couche de règles, et les hooks et règles de lint qui les tiennent en place. Vois comment j'aide les équipes.
AGENTS.md est un routeur, pas une décharge
C'est là que la plupart des équipes se plantent, et c'est une erreur que je refuse de faire dans mon propre travail.
Mon AGENTS.md est un point d'entrée. Chaque agent le lit en premier, par convention. Mais il ne contient pas les règles. Il pointe vers elles. Voici la forme qu'on utilise chez Odisei Play.
AGENTS.md the router, hard cap 1,500 words.ai-rules/ README.md index of every rule file, one line each navigation.md one topic per file state-management.md testing.md ... 66 files todaypackages/ui/CLAUDE.md rules that only apply inside this package
Le routeur a deux parties. D'abord les hard gates : la poignée de règles qui s'appliquent à toute tâche, quoi qu'elle touche. On en a neuf, du genre « ne merge jamais une PR sans une commande explicite dans le tour courant » et « chaque environnement déployé lit la base de production ». Ensuite, une table de routage. Une ligne par famille de règles, et chaque ligne pointe vers une section, pas vers un fichier :
| If you touch | Read first ||--------------------------------------|-----------------------------------------------------------|| Links, back, child exits, new tabs | .ai-rules/navigation.md § "Back-Button Discipline" || Any new feature state | .ai-rules/state-management.md § "Always use useValue" || Writing tests, Storybook, Playwright | .ai-rules/testing.md § "Test Design Philosophy" |
Les fichiers de règles, eux, peuvent être longs. navigation.md fait 831 lignes. Et c'est très bien, parce que personne ne le charge en entier. Chaque titre énonce une règle sous forme de phrase, et le corps en dessous donne le mécanisme et le numéro de l'issue qui en a fait une règle. Quand un agent touche à la navigation arrière, il lit le routeur, suit une ligne, et charge une section. Tout le reste reste hors du context window.
Deux choses empêchent ça de devenir une convention bordélique de plus.
Une règle vit à un seul endroit, chez son propriétaire le plus spécifique. Le routeur ne contient jamais de règle. Un fichier de package ne contient que ce qui s'applique dans ce package. Si deux fichiers disent la même chose, c'est un bug, et le fix est une suppression, pas un troisième fichier. L'agent n'a donc jamais à choisir entre deux règles qui se recouvrent. La structure garantit qu'il n'y en a qu'une.
Et un script fait respecter la forme. yarn check:agent-guidance casse le build quand le routeur dépasse 1 500 mots, ou quand une ligne cite un titre de section qui n'existe pas. Les règles dérivent. Les titres sont renommés. Sans ce contrôle, un pointeur vers rien ressemble exactement à un pointeur vers quelque chose. Le nôtre a démarré à 293 lignes. Il en fait 115 aujourd'hui, et la plupart de mes edits dessus sont des suppressions.
Si tu bosses dans un gros monorepo, tu es en train de te demander comment ça tient quand les sujets se multiplient. Voici ce qui garde notre routeur petit, et l'endroit où j'arrêterais de faire confiance à ma propre expérience.
Le routeur a un budget, et le build le fait respecter. Cible de 800 à 1 200 mots, plafond dur à 1 500. Le nôtre est à 1 480 avec 48 lignes et 9 gates, donc chaque nouvelle ligne doit en pousser une ancienne dehors. C'est le but. Une ligne n'existe que si aucune ligne existante n'atteint déjà ce fichier, et une ligne couvre une famille de règles, pas une règle. « Links, back, child exits, new tabs » est une seule ligne qui atteint une seule section. Et quand une règle peut devenir une règle de lint à la place, elle devient une règle de lint et le paragraphe saute. Quarante des nôtres sont parties comme ça.
L'autre moitié, c'est le scope. Les règles qui ne comptent que dans un package vivent dans le fichier de ce package, et l'agent ne le charge que quand il y travaille. Le routeur racine ne les porte jamais. C'est comme ça que le pattern passe à l'échelle : pas un routeur plus gros, mais un petit routeur par package, la racine ne gardant que les gates qui traversent tout. Notre repo a une app et neuf packages, pour 66 fichiers de règles. Je n'ai pas fait tourner ça sur un monorepo de cinquante services. Si je le faisais, je m'attendrais à un routeur racine avec dix gates et rien d'autre, et chaque service propriétaire de sa table de routage.
Et une tâche qui a besoin de règles venues de plusieurs fichiers à la fois ? Les lignes pointent vers des sections, donc un changement qui touche l'état et la navigation charge deux sections, pas deux fichiers. Chacune fait quelques centaines de mots. Si une tâche a besoin de cinq familles, je le lis comme un signal que le changement est trop large pour une seule pull request, et je le découpe avant que l'agent démarre.
Les fichiers de règles qui débordent
Le fichier de routage reste maigre. Les fichiers de règles par sujet, eux, sont l'endroit où le désordre s'accumule, et ça arrive même quand la structure est bonne.
Tu ajoutes une règle pour la fois où l'agent a pris le mauvais chemin. Puis une autre. Certaines de ces règles contournent des faiblesses de modèle qui disparaissent à la release suivante. D'autres étaient justes autrefois et sont fausses maintenant. D'autres se répètent entre elles, parce que trois règles ont fini par dire la même chose de trois façons.
Les nôtres ont doublé en six mois. C'est en écrivant ce texte que j'ai vu pourquoi. On n'a jamais mis le nettoyage au calendrier. Chaque règle a été ajoutée le jour où quelque chose a mal tourné, et aucune règle n'a jamais été programmée pour être relue. Le fix que j'ajoute est ennuyeux : trente minutes toutes les une ou deux semaines, ouvrir les fichiers que les agents lisent le plus, supprimer ce qui ne mérite plus ses lignes. Le contexte est un budget. Le routeur reste petit par construction. Les fichiers de règles ne restent honnêtes que si quelqu'un est inscrit pour les couper.
Ce qui survit au prochain modèle
Tu es peut-être en train de te dire que toute cette approche est un pansement sur les modèles d'aujourd'hui. Des context windows plus longs, un meilleur suivi des instructions, des outils intégrés, et les fichiers de règles deviennent du poids mort. J'y ai pensé, et je n'y crois pas, pour une raison. La plupart de nos règles ne parlent pas du modèle. Elles parlent de la codebase.
« Chaque environnement déployé lit la base de production » est vrai quel que soit le modèle que tu fais tourner. Pareil pour le graphe d'imports entre packages, pour le fait qu'une étape de quête navigue avec replace et pas push, pour le fait qu'une suppression demande confirmation exactement une fois. Aucune mise à jour de modèle ne rend ces faits obsolètes. Un context window plus grand non plus. Il laisse juste l'agent charger le mauvais fichier plus confortablement, et deux règles qui se contredisent ne se résolvent pas toutes seules parce que la fenêtre a grandi.
Les règles qui vieillissent, ce sont celles qui parlent du comportement du modèle, et elles vieillissent vite. En voici une vraie. Le 14 juillet, on a écrit une règle dure : le modèle cher n'exécute jamais, chaque sous-agent en épingle un moins cher. La raison, c'était une review qui avait lancé sept sous-agents sur le modèle cher et brûlé un dixième d'un quota hebdo en quelques minutes. Dix jours plus tard, le harness est passé à un seul modèle partout, et la règle ne pointait plus vers rien. On l'a supprimée. Et le risque s'est inversé, parce que le nouveau modèle attrapait des sous-agents plus volontiers que l'ancien : la consigne est passée de « délègue par défaut » à « plafonne le fan-out ». Même incident, règle opposée, dix jours d'écart.
C'est pour ça que chaque règle dans nos fichiers porte son précédent, l'issue ou la pull request qui en a fait une règle. Quand un modèle change, je ne relis pas les règles en devinant. Je trie celles qui décrivent un comportement de modèle, je rejoue le précédent sans la règle, et je supprime la règle si le modèle ne prend plus le mauvais chemin. Les règles qui parlent de la codebase restent. Les scripts qui les font respecter restent. Ce dans quoi tu investis, c'est la partie qui survit.
Pour info, le setup tourne sur Claude Code en premier, avec Codex à côté, sur Opus 5 et Fable 5.1 et un modèle GPT pour certains agents de review. Les fichiers de règles sont du markdown que n'importe lequel d'entre eux peut lire. Seuls le nom du fichier routeur et la config propre à chaque outil changent.
Et puisqu'on me demande à quoi ressemble une session d'élagage, voici la plus grosse qu'on ait faite. Le 11 mars, deux jours après le split en monorepo, un seul commit : 48 fichiers changés, 83 lignes ajoutées, 8 400 supprimées. Est parti un dossier skills/ entier, 7 000 lignes copiées de la doc des plugins d'Expo. Le modèle connaît déjà Expo, et Expo tient cette doc à jour mieux que nous. Est parti un fichier de standards de code qui répétait le linter. Les règles api sont passées de 472 lignes à 84 en gardant les règles et en coupant les exemples. Et une contradiction sur la façon de fusionner les noms de classes, énoncée de deux façons différentes dans deux fichiers, a été réglée en en choisissant une.
La plupart des élagages sont plus petits que ça, et ils ne parlent pas du modèle du tout. En juillet, une règle dans notre fichier SEO disait encore qu'un artiste a besoin de deux morceaux pour être indexé. Le produit avait shippé tous les artistes indexables des semaines plus tôt. La règle contredisait le code, et un commit de trois lignes a corrigé ça. Le modèle n'avait pas changé. Le code, si. C'est le cas le plus fréquent, et c'est celui pour lequel la session bimensuelle existe.
L'autonomie est un curseur, pas une vertu
Le but, ce n'est pas plus d'autonomie. L'autonomie est un curseur, pas une vertu, et la monter avant que les garde-fous existent, c'est comme ça qu'on shippe un désordre en prod.
Ce qui fait gagner l'autonomie :
Une spec qui dit ce que l'agent va faire avant qu'il tourne, pour avoir quelque chose à vérifier.
Des hooks et des règles de lint qui agissent comme une CI pour l'agent : les contrôles mécaniques qui le chopent en train de casser ses propres règles au moment où ça arrive. On a 40 règles ESLint maison, chacune avec son fichier de test, et un script qui casse le build quand un composant est copié au lieu d'être réutilisé.
Une boucle de review assez rapide pour exister vraiment, pour que « revu par un humain » ne devienne pas un goulot que tout le monde contourne.
L'équipe garde le contrôle des décisions et du code qui sort. L'agent fait plus de travail, pas plus de décisions.
La boucle de review en pratique
« Une boucle de review rapide », c'est facile à approuver et facile à sauter. Voici la nôtre, avec les chiffres.
L'agent review le diff. L'humain lit le rapport. C'est ça, le partage. Quand une pull request est prête, une seule commande lance plusieurs passes de review sur le diff local : une passe correction, une passe style, et des audits écrits pour cette codebase, du genre « as-tu ajouté un bouton alors qu'il en existe un dans le package ». Rien n'est posté sur GitHub. Les passes se replient en un rapport compilé avec un plan de fix, groupé par sévérité. Puis ça s'arrête et ça attend un humain.
L'humain lit le rapport, pas huit cents lignes de diff. Il approuve ou rejette chaque groupe. Ce n'est qu'ensuite que les fixes tournent, un commit par groupe. Le rapport est l'artefact de review, et lire un rapport prend dix minutes là où lire le diff en prenait une heure. C'est ce qui rend la boucle assez rapide pour avoir lieu à chaque fois.
Deux détails comptent plus qu'ils n'en ont l'air. Le modèle qui review n'est pas le modèle qui a écrit le code. Un reviewer qui note sa propre sortie partage ses angles morts par construction, donc les passes de détection tournent sur le modèle d'un autre vendeur. Et chaque review se termine en réécrivant dans les fichiers de règles. Si la review a trouvé une classe d'erreur, la règle qui l'aurait attrapée y entre, pour que la fois d'après une règle de lint ou un hook l'attrape avant qu'un humain ne lise quoi que ce soit.
Qui fait ça ? La personne qui merge. Dans notre équipe, ce sont les deux leads, qui écrivent aussi neuf PRs sur dix. Les pull requests de la designeuse passent par la même commande, lancée par le lead qui merge.
Ce que « assez rapide » veut dire chez nous, sur 815 pull requests mergées en 2026 : le temps médian entre l'ouverture et le merge est entre 1,5 et 8,5 heures selon le mois, et trois sur quatre mergent en moins de 24 heures. C'est sur environ 200 pull requests par mois, à quatre personnes. Le mois où ça a dérivé à 8,5 heures, c'est août, quand on préparait une release, et je l'ai vu parce que le chiffre a bougé. Une boucle de review que tu ne peux pas mesurer est une boucle que tu contourneras sans même le remarquer.
À quoi ressemble le résultat
Le meilleur signal que je peux montrer, c'est ce qui a changé pour le reste de l'équipe, pas pour l'agent.
Dans la codebase d'Odisei Play, le gain, c'était notre designeuse. En mars, on a découpé l'app en monorepo avec un package packages/ui et Storybook, par-dessus des fichiers de règles qu'on écrivait depuis janvier. Elle est arrivée la même semaine. Six mois plus tard, elle a 277 commits sur le package UI, 182 composants et 177 stories, shippés à travers 40 pull requests revues. Le package est passé de zéro à 390 composants. Le débit de l'équipe est passé d'un pic mensuel de 20 PRs mergées en 2025 à 200 en juillet 2026.
Tout n'a pas bougé dans le bon sens. Les fichiers de modales côté app ont augmenté, pas diminué. Les fichiers de règles ont grossi, comme je l'ai dit plus haut. Et je ne peux pas prouver de causalité à partir d'un git log, seulement que la chronologie colle. Mais le design a arrêté de se dégrader en route vers la prod, et je peux montrer les commits qui le montrent.
C'est le vrai test de la préparation agentique. Pas de savoir si l'agent est plus rapide. De savoir si les humains de l'équipe peuvent soudain travailler là où ils étaient bloqués.
Ce que ça a coûté
Tu lis l'histoire qui marche et tu te demandes ce qu'elle a demandé. Voici ce que ça nous a demandé, d'après l'historique des commits, parce que je n'ai pas tenu de feuille d'heures.
La migration elle-même, c'était un ingénieur, moi, pendant environ une semaine. La première config monorepo a atterri le 6 mars. Le guide de démarrage pour la designeuse le 10 mars. Le déplacement a touché 1 645 fichiers, surtout des renommages, et j'ai utilisé des agents pour la partie mécanique. Puis trois semaines de plus à durcir tout ça, que j'aurais sous-estimées si tu me l'avais demandé avant : Storybook sur web et natif, les watch folders de Metro, les patches Yarn, les configs Netlify et EAS qui supposaient une seule app. Compte un mois de temps d'un lead, sur une codebase où je travaillais déjà depuis deux ans. Sur un repo que tu ne connais pas, prévois plus.
Le coût courant, ce sont les fichiers de règles. On y pose entre 40 et 90 commits par mois. Juillet est monté à 247, le mois où on a reconstruit le routeur. C'est ça, la charge de maintenance, et elle ne disparaît pas. Programme la session de nettoyage décrite plus haut, et donne-lui un propriétaire. C'est ce que je fais en ce moment. Si personne ne la porte, les règles pourrissent et tu te retrouves avec des agents qui devinent avant un an.
Si ton repo est un bordel aujourd'hui, la première étape que je prendrais, c'est le découpage : un monorepo avec un package par partie du produit, ce que les gens appellent un découpage par domaine. Ça t'oblige à nommer les frontières, ce qui est exactement ce qui manquait à l'agent. Et ça te laisse réparer un package à la fois plutôt que tout le repo d'un coup. Commence par le package qui a quelqu'un qui l'attend. Pour nous, c'était packages/ui, parce que la designeuse attendait.
Combien de temps avant de voir quelque chose ? Notre designeuse a poussé sa première story Storybook deux jours après le split. Sa première pull request a mergé dix jours après. Les chiffres au niveau de l'équipe ont mis un mois à bouger.
Si ton repo se bat contre tes agents, c'est un problème réparable. Dis-moi le truc que tes agents ont fait cette semaine et qui t'a fait grincer des dents. Parlons de ton repo.