Gérer un monorepo avec l'agent
Un monorepo concentre des dizaines de modules et des conventions hétérogènes. Scopage du contexte, stratégie CLAUDE.md hiérarchique, navigation ciblée et pièges courants : comment cadrer l'agent pour qu'il travaille efficacement sans dériver vers le mauvais module.
Le défi du monorepo pour un agent
Un monorepo héberge plusieurs projets ou modules dans un seul dépôt Git. C’est courant dans les grandes bases .NET (une solution avec 20+ projets) ou les organisations qui centralisent front, back et libs. Pour un humain, la navigation est déjà complexe. Pour un agent, c’est un triple problème :
- Volume de code — des milliers de fichiers. L’agent ne peut pas tout charger dans sa fenêtre de contexte. Il doit chercher, filtrer, et se limiter au strict nécessaire.
- Conventions hétérogènes — le module
Api.Gatewayutilise des contrôleurs MVC, le moduleWorkersutilise des hosted services, le moduleShared.Kernelsuit du DDD pur. Les conventions de nommage, les patterns et les dépendances autorisées diffèrent. - Risque de cross-contamination — sans cadrage, l’agent peut modifier un module voisin « pour corriger une dépendance », appliquant les conventions du module A au module B.
Point clé : Dans un monorepo, le contexte est le levier principal (LP1). L’agent ne peut pas tout voir : il faut lui indiquer quoi regarder, quoi ignorer, et quelles règles s’appliquent au périmètre courant.
Scopage du contexte : un module à la fois
Le principe fondamental : une session agent = un module. Plutôt que de lancer l’agent à la racine du monorepo en lui disant « ajoute un endpoint », on cadre le périmètre :
- Ouvrir le sous-dossier — lancer l’agent depuis
src/Api.Orders/plutôt que depuis la racine. Cela limite naturellement les fichiers visibles. - Nommer explicitement le module — « Dans le module
Api.Orders, ajoute un endpoint GET/orders/{id}/status». L’agent sait où chercher les fichiers existants et où créer les nouveaux. - Interdire les modifications hors périmètre — « Ne modifie aucun fichier en dehors de
src/Api.Orders/ettests/Api.Orders.Tests/». C’est une contrainte explicite qui évite la dérive.
Ce scopage applique le principe du 80-20 Planning : on investit quelques secondes à cadrer le périmètre pour éviter des minutes de corrections croisées.
Attention — piège courant : lancer l’agent à la racine du monorepo en mode AFK (sans supervision). Il va explorer, ouvrir des dizaines de fichiers, saturer son contexte, et finir par appliquer un changement au mauvais module. Le mode AFK dans un monorepo exige un scopage encore plus strict qu’en interactif.
Stratégie CLAUDE.md pour monorepo
Les instructions persistantes (CLAUDE.md, .cursorrules, etc.) sont le levier LP12 — et dans un monorepo, leur organisation est critique. La bonne pratique : une architecture hiérarchique.
Fichier racine
Le CLAUDE.md à la racine du dépôt contient les règles transversales :
- Structure globale du monorepo (quels modules existent, leur rôle)
- Conventions communes (format de commit, règles de PR, CI/CD)
- Commandes build/test/lint globales
- Interdictions globales (ex : « ne jamais référencer directement un projet d’un autre module sans passer par le Shared.Kernel »)
Fichiers par module
Chaque module possède son propre fichier d’instructions (src/Api.Orders/CLAUDE.md) avec :
- Conventions spécifiques au module (patterns, nommage, couches)
- Commandes build/test locales au module
- Dépendances autorisées et interdites
- Exemples de code représentatifs du style attendu
Point clé : L’agent qui travaille dans
src/Api.Orders/lit le CLAUDE.md racine + le CLAUDE.md local. Il hérite des règles globales et les spécialise avec les conventions du module. C’est exactement le même principe que l’héritage de configuration dans un.editorconfig.
Navigation et découverte
Dans un monorepo, l’agent doit trouver le bon code avant de le modifier. Plusieurs techniques facilitent cette découverte :
- Carte du dépôt dans le CLAUDE.md racine — un arbre simplifié qui liste chaque module, son rôle et ses dépendances. L’agent s’y réfère avant de chercher un fichier.
- Commandes de recherche ciblées — documenter dans les instructions persistantes les commandes grep/ripgrep utiles : « Pour trouver un handler, cherche
: IRequestHandlerdanssrc/Api.Orders/». - Conventions de nommage strictes — si chaque handler s’appelle
{Action}{Entity}Handler.cs, l’agent peut déduire le fichier cible sans explorer tout l’arbre. - Tests comme documentation vivante — les tests d’un module montrent les cas d’usage et les contrats. Pointer l’agent vers
tests/Api.Orders.Tests/comme référence des comportements attendus.
En architecture hexagonale, la structure en couches (Domain, Application, Infrastructure, Presentation) offre un guide de navigation naturel : l’agent sait que les entités sont dans Domain/, les use cases dans Application/, les adaptateurs dans Infrastructure/.
Pièges courants et parades
| Piège | Cause | Parade |
|---|---|---|
| Édition cross-module involontaire | L’agent « corrige » une dépendance dans un module voisin | Contrainte explicite : « ne modifie rien en dehors de src/X/ » |
| Mauvaises conventions appliquées | L’agent voit des fichiers d’un autre module dans son contexte et imite leur style | CLAUDE.md par module + scopage du répertoire de travail |
| Context rot accéléré | Le monorepo charge trop de fichiers dans la fenêtre de contexte | Sessions courtes, un module par session, nettoyage régulier du contexte |
| Build/test du mauvais module | L’agent lance dotnet test à la racine (tous les projets) au lieu de cibler le module | Documenter la commande locale dans le CLAUDE.md du module |
| Dépendance circulaire introduite | L’agent ajoute une référence entre modules sans connaître le graphe de dépendances | Documenter le graphe autorisé dans le CLAUDE.md racine + valider avec dotnet build après chaque ajout |
Point clé : Le feedback console (LP5) est particulièrement important dans un monorepo : un
dotnet buildciblé sur le bon module après chaque modification permet à l’agent de détecter immédiatement s’il a cassé quelque chose — y compris une dépendance qu’il n’aurait pas dû toucher.