Accueil / Articles

Structurer les projets Claude Code afin que les agents restent concentrés dans de grands dépôts

Six modèles pour de grands bases de code : fichiers CLAUDE.md hiérarchisés, compétences, sous-agents, règles de refus, arbres de travail sparses et plugins de language-server qui maintiennent le contexte de l’agent propre.

Éditeur : REACTAPP.TOP10 min de lecture2372 mots

Sources: How I Structure Claude Code Projects So Agents Don’t Get Lost in Large Codebases

Imaginez une demande simple adressée à Claude Code : corriger un bug de timeout de session dans le middleware d’authentification. La véritable correction ne nécessite que trois lignes. Pourtant, avant d’y parvenir, l’agent parcourt le système de journalisation, l’historique des migrations, la moitié des points d’entrée API et, pour des raisons inexplicables, les configurations Terraform. Après avoir lu le 147e fichier, le développeur finit par appuyer sur Escape.

Le modèle n’est pas la cause d’une telle situation. C’est la structure du projet. Chaque convention, commande de compilation et décision architecturale avait été regroupée dans un seul fichier CLAUDE.md de 340 lignes, que l’agent lisait fidèlement au démarrage avant d’utiliser le reste de son temps à explorer du code sans rapport avec l’authentification.

C’est ce qui se passe lorsque l’on traite un agent comme un nouvel employé qui doit lire l’intégralité du manuel de l’entreprise avant de pouvoir corriger une faute d’orthographe. Il a besoin du bon contexte au bon moment, et non de tout en même temps. Suivent six modèles permettant de résoudre ce problème, ainsi qu’une méthode pour déterminer quand il convient d’en utiliser chacun.

Pourquoi le contexte s’épuise rapidement

La fenêtre de contexte de Claude Code, d’environ 200 000 tokens sur le modèle utilisé ici, semble énorme jusqu’à ce que l’on énumère ce qui la réduit : chaque message envoyé, chaque fichier lu, toutes les sorties de commande, chaque instruction CLAUDE.md et tous les notes mémorielles enregistrées automatiquement. Un token correspond à environ quatre caractères.

Dans une grande base de code, 50 000 tokens peuvent disparaître avant même qu’une ligne de code ne soit écrite. Un fichier CLAUDE.md de 300 lignes coûte environ 1 200 tokens, un fichier de 500 lignes en coûte 2 000 de plus, et l’utilisation de grep ou find pour effectuer des recherches augmente encore ce chiffre. Au moment où l’agent comprend la tâche, la moitié de la fenêtre de travail peut déjà avoir disparu. À mesure qu’elle se remplit, les performances chutent : les instructions précédentes sont oubliées, les fichiers doivent être relus, et des erreurs apparaissent qui n’auraient pas surgi avec un contexte propre. Des fenêtres de travail plus grandes ne sont pas la solution ; il s’agit plutôt d’éviter d’inclure des éléments irrelevants dans cette fenêtre.

Modèle 1 : fichiers CLAUDE.md hiérarchisés

La première erreur consiste à mettre tout le contenu dans le fichier racine : commandes de construction pour l’API, modèles de composants frontend, règles de migration, listes de contrôle de déploiement et conventions de test, tout ensemble.

Claude Code lit les fichiers CLAUDE.md depuis le répertoire de travail jusqu’à la racine du dépôt au démarrage d’une session. Lorsqu’on le lance dans un sous-répertoire, il prend en compte le fichier de ce dossier ainsi que tous les fichiers des répertoires parents jusqu’à la racine. Il faut donc séparer le contenu en fonction du scope :

monorepo/
  CLAUDE.md                     # repository-wide rules only
  packages/
    api/
      CLAUDE.md                 # API-specific conventions
      .claude/skills/
      src/
    web/
      CLAUDE.md                 # frontend-specific conventions
      .claude/skills/
      src/
    shared/
      CLAUDE.md                 # shared library conventions
      src/

Le fichier racine ne contient que la structure du dépôt et les conventions de commit, soit environ 40 lignes, tandis que chaque package intègre ses propres détails spécifiques à son stack. Lorsque l’agent est lancé dans packages/api/, il charge le fichier racine ainsi que celui relatif à l’API, sans jamais prendre en compte les conventions front-end. En passant à packages/web/, la même logique fournit un ensemble différent de contexte.

Un fichier racine pourrait ressembler à ceci :

# Repository Structure

This is a monorepo with three packages under packages/:

- packages/api: Node.js REST API with Express, TypeScript, PostgreSQL
- packages/web: React frontend with Vite, TypeScript, TailwindCSS
- packages/shared: shared TypeScript utilities

Run commands from the package directory, not the monorepo root.
Each package has its own tsconfig.json, package.json, and test suite.

# Commit Conventions

- Prefix commits with the package name: "api: fix session timeout"
- One commit per logical change
- Run tests before committing

Et le paquet API ne contient que ce qui est essentiel à l’intérieur de ce paquet :

# API Package

This is the REST API server.

## Commands

- Run tests: `npm test` (uses Vitest)
- Run dev server: `npm run dev` (port 3001)
- Database migrations: `npm run migrate`
- Environment variables: copy `.env.example` to `.env`

## Code Patterns

API routes are in src/routes/. Each route file exports an Express router.
Database queries use Knex in src/db/. Never write raw SQL in route handlers.

## Testing

Tests are in src/__tests__/ mirroring the src/ directory.
Use supertest for HTTP assertions, not raw fetch.
Always wrap database tests in a transaction that rolls back.

Dans cet exemple, l’effet est de 80 lignes de contexte chargé au lieu de 340, et la correction liée à l’authentification concerne 12 fichiers plutôt que 147.

Modèle 2 : compétences pour un savoir sur demande

Même une configuration en couches peut devenir encombrante. Les modèles de test, les listes de contrôle de déploiement et les guides de débogage sont des procédures et non des faits ; l’agent n’a pas besoin d’eux au démarrage, seulement lorsque la tâche l’exige. C’est là le rôle des compétences.

Les compétences se trouvent sous .claude/skills/ et sont chargées lorsque Claude les juge pertinentes, ou lorsqu’elles sont invoquées explicitement via une commande. La description d’une compétence indique quand l’utiliser, tandis que son contenu contient le texte réel. Ici, les directives de test quittent le fichier API pour être intégrées dans une compétence :

# .claude/skills/api-testing/SKILL.md

---
name: api-testing
description: Testing patterns for the API package. Use when writing or modifying tests in packages/api/.
---

## Test Structure

Tests are in `src/__tests__/` mirroring the `src/` directory structure.
Each route file has a corresponding `.test.ts` file.

## Running Tests

- All tests: `npm test`
- Single file: `npm test -- src/__tests__/routes/users.test.ts`
- Watch mode: `npm test -- --watch`

## Test Utilities

- `src/__tests__/helpers/db.ts`: provides `setupTestDb()` and `teardownTestDb()`
- `src/__tests__/helpers/auth.ts`: provides `createTestUser()` and `getAuthToken()`

## Patterns

- Use `supertest` for HTTP assertions, not raw fetch
- Always wrap database tests in a transaction that rolls back
- Mock external services in `src/__tests__/mocks/`

Demandez des tests sur le middleware d’authentification et l’agent chargera la compétence de test et suivra ses schémas. Demandez-lui de corriger le bug de délai d’attente, mais la compétence restera non chargée, car les tests ne sont pas pertinents.

Les compétences conviennent également aux workflows répétables. Une compétence appelée avec /fix-pr-comments pourrait lire les commentaires de revue sur une demande de pull GitHub, déterminer les modifications demandées, modifier le code, exécuter des tests et soumettre les changements. Toute la procédure est contenue dans la compétence et non dans un CLAUDE.md qui consomme du contexte à chaque session.

Règle générale : les faits nécessaires à chaque session sont placés dans CLAUDE.md, tandis que les procédures et les documents de référence deviennent des compétences.

Modèle 3 : sous-agents pour une exploration isolée

Même avec des couches et des compétences supplémentaires, l’exploration devient trop complexe. La question « Comment fonctionne l’authentification ? » peut entraîner 40 lectures de fichiers, car l’agent suit les étapes de connexion, de stockage de session, de renouvellement de token et de déconnexion ; tout ce texte reste dans la conversation principale après réponse, polluant le reste de la session.

Un sous-agent s’exécute dans sa propre fenêtre de contexte, effectue l’exploration et ne renvoie qu’un résumé. Les lectures de fichiers et les résultats de grep restent dans son contexte. Le thread principal reçoit uniquement le résumé, ce qui maintient la conversation propre. Une demande de recherche pourrait ressembler à ceci :

Use a subagent to investigate how our authentication system handles
session timeout and token refresh. Report back what files are involved
and how the flow works.

Le sous-agent lit ce dont il a besoin, suit le flux et fait un rapport, de sorte que la conversation principale reçoit une explication claire d’environ 200 mots plutôt que le contenu de 40 fichiers. Les sous-agents aident également à la révision après une mise en œuvre :

Use a subagent to review the session timeout fix for edge cases
and consistency with our existing auth patterns.

L’agent subalterne chargé de la revue voit la différence telle qu’elle est, sans les explications ayant entraîné le changement.

Un point d’attention : les résumés perdent en détails. Si vous avez besoin des numéros de ligne exacts ou d’extraits de code, l’agent principal devra peut-être relire les fichiers que l’agent subalterne a déjà consultés. Les agents subalternes se concentrent sur l’implémentation principale tandis que l’exploration a lieu ailleurs.

Modèle 4 : laisser les fichiers générés et fournis inchangés

Tout ce qui est listé dans .gitignore est ignoré par défaut, ce qui exclut node_modules/, dist/ et build/ des résultats de recherche. Cependant, certains codes générés ou fournis sont tout de même commités : des définitions protobuf compilées, des SDK fournis, des types GraphQL générés. L’agent ouvrira volontiers ces fichiers s’ils apparaissent lors d’une recherche, bien que cela ne devrait jamais se produire.

Les règles de refus de permission empêchent la lecture des motifs de fichiers sélectionnés. La structure générale est indiquée ci-dessous, et la syntaxe exacte doit être vérifiée dans la documentation correspondant à votre version de Claude Code :

# packages/api/.claude/settings.json

{
  "permissions": {
    "deny": [
      "Read(./**/dist/**)",
      "Read(./**/build/**)",
      "Read(./**/*.generated.*)",
      "Read(./vendor/**)"
    ]
  }
}

Lorsqu’une recherche pour « GraphQL resolvers » trouve un fichier de schéma généré, le résultat s’affiche mais l’outil de lecture répond par une permission refusée, et l’agent demande généralement où se trouve la source réelle. Un effet secondaire à connaître : les mêmes règles empêchent également les lectures demandées manuellement dans la fenêtre de chat. Si vous lui demandez d’ouvrir src/schema.generated.ts, la demande est rejetée ; il faut alors supprimer temporairement cette règle. L’avantage est que le bruit généré reste en dehors du contexte et que l’agent est orienté vers la source réelle.

Pattern 5 : worktrees sparses

Claude Code peut créer des worktrees git pour un travail parallèle ou isolé, et par défaut chacun d’eux télécharge l’ensemble du répertoire. Dans un monorepo, c’est gaspilleur, car une correction d’API n’a pas besoin du frontend.

Le mécanisme sparse-checkout de Git n’écrit que les répertoires sélectionnés sur le disque, laissant le reste dans l’index, et la configuration worktree.sparsePaths s’appuie sur ce principe. Vérifiez la clé de configuration selon la documentation de votre version :

# packages/api/.claude/settings.json

{
  "worktree": {
    "sparsePaths": [
      ".claude",
      "packages/api",
      "packages/shared"
    ]
  }
}

Les worktrees ne contiennent alors que .claude/, packages/api/ et packages/shared/, ainsi que des fichiers racine tels que package.json et tsconfig.base.json. Ils sont plus rapides à créer et occupent moins d’espace sur le disque. Si l’agent tente de lire un fichier qui n’a pas été vérifié, git génère une erreur et l’agent demande généralement quels dossiers existent. Le mode de vérification sparse-checkout nécessite git 2.25 ou une version ultérieure, ce que git --version permet de confirmer.

Modèle 6 : intelligence du code plutôt que balayage des fichiers

Localiser où une fonction est définie ou utilisée dans une grande base de code requiert de nombreuses appels à grep et lectures de fichiers, car l’agent examine les résultats, ouvre les candidats possibles et continue ses recherches.

Les plugins d’intelligence de code relient l’agent à un serveur de langage. Au lieu de scanner, ils demandent où refreshAuthToken est défini et reçoivent immédiatement le fichier ainsi que le numéro de ligne correspondant. Pour les projets TypeScript, le plugin officiel LSP s’installe de cette manière, bien que la syntaxe d’installation varie selon la version ; veuillez donc consulter la documentation :

/plugin install typescript-lsp@claude-plugins-official

Demandez où refreshAuthToken est utilisé, et l’agent interroge le serveur de langage pour obtenir la réponse suivante :

src/middleware/auth.ts:47
src/routes/users.ts:103
src/__tests__/auth.test.ts:22

Aucune lecture de fichier n’était nécessaire, ce qui rend les réponses plus rapides et maintient le contexte propre. Le plugin signale également les erreurs de type après les modifications sans avoir à exécuter tsc, de sorte que les erreurs apparaissent immédiatement. Il s’associe bien aux règles de refus : celles-ci bloquent le bruit, tandis que l’intelligence de code réduit la nécessité de scanner ce qui reste.

Tous les six ensemble

Avec chaque motif appliqué, la correction initiale du bug se déroule à peu près comme suit :

  1. Démarrer Claude depuis packages/api/.
  2. Il charge le fichier CLAUDE.md racine (40 lignes) ainsi que celui relatif à l’API (50 lignes).
  3. On lui demande de corriger le bug de délai de session dans le middleware d’authentification.
  4. Il envoie un sous-agent pour examiner le flux d’authentification, lequel lit environ 20 fichiers dans son propre contexte.
  5. Le sous-agent signale que la logique de délai se trouve dans src/middleware/auth.ts à la ligne 47 et qu’un opérateur de comparaison incorrect est utilisé.
  6. L’agent principal lit ce fichier, corrige la faille, écrit un test et le lance, modifiant ainsi environ quatre fichiers dans le contexte principal.

Dans cette illustration, on voit qu’avant et après il y a 147 lectures de fichiers, trois tentatives échouées et 35 minutes, contre environ quatre lectures, une tentative réussie et 6 minutes. Considérez cela comme l’anecdote d’un développeur et non comme une référence de performance, mais c’est la direction qui compte : la structure a éliminé les obstacles entre la question et la réponse.

Sélectionner ce dont vous avez besoin

Chaque projet n’a pas besoin des six éléments. Un seul répertoire contenant 50 fichiers n’a pas besoin de fichiers hiérarchisés ou d’arbres de travail spars. Lorsque la base de code dépasse ce qu’une seule personne peut retenir en tête, ces éléments commencent à se révéler utiles :

  • Fichiers CLAUDE.md hiérarchisés : utiles lorsque vous défilez pour passer des sections non pertinentes du fichier racine, ou lorsque des collègues demandent pourquoi le fichier API contient des règles front-end.
  • Compétences : utiles lorsque une section de CLAUDE.md est devenue une procédure ou une liste de contrôle, ou lorsque vous collez constamment les mêmes instructions dans le chat.
  • Sous-agents : utiles lorsque une enquête se termine par « lisez ces 30 fichiers », ou lorsque vous souhaitez un examen sans biais.
  • Règles de refus : utiles lorsque l’agent continue d’ouvrir du code généré ou fourni en dépit de .gitignore.
  • Arbres de travail spars : utiles lorsque le dépôt monolithique met plus de 10 secondes à être déverrouillé, ou lorsque vous créez de nombreux arbres de travail en parallèle.
  • Les plugins de code intelligent sont utiles lorsque vous travaillez dans un langage à typage statique et que l’agent passe plus de temps à rechercher des définitions qu’à écrire du code.
  • L’objectif n’est pas d’appliquer tout cela aveuglément. Il s’agit de remarquer quand le contexte devient encombré et de choisir le modèle qui le nettoie. D’autres structures peuvent également fonctionner pour de grands bases de code, mais ces six modèles transforment un agent qui lit tout en un agent qui lit uniquement les fichiers appropriés, écrit la correction et s’arrête.