MaleusMaleus
Ingénierie15 mai 2026· 10 min de lecture

Context Engineering pour Coding Agents : pourquoi un seul CLAUDE.md ne suffit pas

Pourquoi un seul CLAUDE.md ne suffit pas sur une vraie codebase, et la structure en 3 couches qu'on utilise chez Maleus pour injecter aux agents les bonnes instructions au bon moment.

Adrien Maret
Adrien MaretCTO
Context Engineering pour Coding Agents : pourquoi un seul CLAUDE.md ne suffit pas
Sur cette page

Le Context Engineering, c'est l'art d'injecter à un agent la bonne information au bon moment pour qu'il code correctement.

Aujourd'hui, la plupart des équipes qui s'attaquent au sujet balancent toutes leurs instructions dans un gros CLAUDE.md à la racine du repo. Ça marche pour un prototype, ça craque dès que la codebase grossit.

Sur une vraie application, il faut 3 niveaux distincts d'instructions, chacun déclenché à un moment différent du workflow de l'agent. Sans cette stratification, on dilue tellement le contexte que le LLM finit par ignorer les règles les plus importantes.

Dans cet article, je détaille les 3 couches qu'on utilise chez Maleus pour générer des applications en quasi-autonomie sur la partie code.

Pourquoi 3 niveaux et pas un seul

Le contexte d'un LLM est limité et chaque token en dilue les autres.

Les instructions ont des scopes différents:

  • scope repo : outillage, structure du projet, environnement, ports, scripts → c'est le rôle du CLAUDE.md à la racine
  • scope domaine : métier d'un domaine fonctionnel, flows, fichiers clés → c'est le rôle des AGENT.md par module
  • scope stack technique : conventions de code par framework / par type de fichier (Drizzle, Nest.js, patterns repository) → c'est le rôle des coding rules

Si on charge tout d'un coup, on récolte du bruit, des contradictions entre règles, et de la dilution.

La solution, c'est de stratifier. Chaque couche est chargée au moment où elle est pertinente.

Niveau 1 - Coding rules         (chargé à chaque édition de fichier)
Niveau 2 - CLAUDE.md            (chargé une fois en début de session)
Niveau 3 - AGENT.md par module  (chargé quand l'agent explore un domaine)

L'ordre de grandeur chez Maleus aujourd'hui:

$ cloc .claude/rules
40 files, 8 437 lignes de markdown (~210 lignes/rule)

$ find apps/studio -name "AGENT.md" | xargs cloc
97 files, 7 855 lignes de markdown (~80 lignes/AGENT.md)

$ cloc apps/studio --include-lang=TypeScript
1 843 files TypeScript, 209 180 lignes de code

Ce qui donne deux ratios intéressants:

  • 1 ligne de doc-pour-agent pour ~13 lignes de code
  • 1 ligne d'AGENT.md pour ~27 lignes de code dans le module qu'il décrit

Si on balançait ces 16 000 lignes dans un seul gros CLAUDE.md, on cramerait l'équivalent de plusieurs dizaines de milliers de tokens à chaque prompt, dont 95% sans rapport avec la tâche en cours. La stratification, c'est ce qui rend cet investissement de contexte tenable au lieu d'imbuvable.

Niveau 1 : les coding rules, déclenchées par pattern matching

C'est la couche la plus granulaire et celle qui a le plus d'impact sur la qualité du code généré.

Une règle par type de fichier

Chez Maleus, chaque fichier backend est suffixé par son rôle:

  • <Entity>.table.ts → définition de table Drizzle
  • <Entity>.repository.ts → accès BDD
  • <Entity>.service.ts → logique métier
  • <Entity>.controller.ts → endpoints Nest.js

À chaque suffixe correspond une rule dédiée, déclenchée par un glob précis dans la propriété paths du frontmatter:

# .claude/rules/backend/backend-tables.md
name: "backend-tables"
paths: "**/{backend,studio-backend}/**/*.table.ts"

Ce découpage par glob permet de charger uniquement la rule pertinente au moment où l'agent touche un fichier de ce type.

Edit flow : pattern matching déterministe sur le read

L'agent lit toujours un fichier avant de l'éditer. Le path passé au read sert de clé au pattern matching.

read("src/features/companies/data/Companies.repository.ts")
  → match "**/{backend,studio-backend}/**/*.repository.ts"
  → injection de backend-repositories.md dans le contexte
edit("Companies.repository.ts", oldString, newString)
  → le newString respecte la règle qu'on vient d'injecter

C'est du pattern matching, donc 100% déterministe, sans aléa LLM possible.

Write flow : le problème du fichier qui n'existe pas encore

Cette mécanique tombe à l'eau quand l'agent crée un nouveau fichier.

Pourquoi ? Parce qu'il n'a aucune raison de faire un read avant, le fichier n'existe pas. Il génère directement write("Foobar.table.ts", content), et le content est déjà dans le buffer avant qu'on ait pu pattern-matcher quoi que ce soit.

Le pattern matching a donc un trou dans la raquette : pour la création de fichier, il faut un autre mécanisme.

Skills : l'évaluation par le LLM en complément

Les skills sont des instructions que le LLM décide lui-même de charger en évaluant leur description.

Le mécanisme:

  • chaque skill a une description qui explique quand le charger
  • on inclut explicitement le pattern dans la description: "à chaque fois que tu écris un fichier matchant **/*.table.ts"
  • on rajoute CRITICAL INSTRUCTION YOU MUST ALWAYS pour appuyer la priorité

Chez nous, la rule (.claude/rules/backend/backend-tables.md) et le skill (.claude/skills/backend-tables/SKILL.md) sont le même fichier via hardlink. Le frontmatter et le contenu sont identiques, seul le mécanisme de déclenchement change: pattern matching pour le edit flow, évaluation LLM pour le write flow.

Le frontmatter sert donc les deux canaux simultanément:

---
name: "backend-tables"
paths: "**/{backend,studio-backend}/**/*.table.ts"
description: Contains guidelines for defining database tables using Drizzle
  ORM with PostgreSQL. [...] CRITICAL INSTRUCTION You MUST ALWAYS use this
  skill before editing/writing any file matching
  "**/{backend,studio-backend}/**/*.table.ts".
---

En pratique le skill se déclenche presque toujours, mais ce n'est jamais 100% puisqu'aucun mécanisme déterministe ne peut intercepter une création de fichier avant la génération du contenu.

CRITICAL INSTRUCTION, à utiliser avec parcimonie

Le mot-clé CRITICAL INSTRUCTION YOU MUST ALWAYS est un canal de priorisation propre aux modèles Anthropic qui fonctionne par contraste : si on l'écrit partout, plus rien n'est prioritaire.

Surtout, écrire CRITICAL INSTRUCTION est le signe qu'on a besoin de déterminisme, pas de prompt engineering. Le prompt engineering augmente la probabilité qu'une instruction soit respectée ; le déterminisme la garantit. Dès qu'un mécanisme déterministe est possible (pattern matching, hook, lint, test), il faut le préférer. Le skill est l'exception assumée: rien ne permet d'intercepter une création de fichier avant la génération.

Construire les règles empiriquement

Inutile d'écrire toutes les coding rules d'un coup. On finit avec des règles génériques, jamais déclenchées, ou pire, qui se contredisent.

L'approche qui marche est inverse. On démarre avec quelques règles essentielles (structure du backend, naming des méthodes, format des erreurs), puis on observe l'agent et à chaque erreur récurrente, on enrichit la rule correspondante.

Autant laisser Claude Code rédiger ces ajouts : les modèles Anthropic sont fine-tunés pour ça. On lui donne la correction ("dans ce repository tu aurais dû utiliser X au lieu de Y") et il met la rule à jour. On relit toujours, et on garde les règles courtes : une rule trop longue se dilue dans le contexte.

Chaque correction humaine devient une règle qui évite la même erreur la fois suivante.

Cette approche en trois couches est l'un des éléments clé qui permettent à Maleus de générer des applications prêtes à être déployées en production.

Niveau 2 : CLAUDE.md, la plomberie du projet

Un seul fichier CLAUDE.md à la racine du repo, chargé une fois au démarrage de la session.

Il répond à trois questions: où je suis, quels outils j'ai, quel est l'environnement.

Concrètement on y trouve:

  • le type d'environnement (cloud sandbox, dev container, etc.)
  • les commandes dispo (lint, test, build)
  • les ports utilisés (frontend 14001, backend 14002)
  • la présence d'une base de données et comment y accéder
  • les scripts npm utiles

Comme ce fichier est chargé pour toute la session, il faut le garder court : chaque ligne reste dans le contexte de toutes les requêtes.

Niveau 3 : AGENT.md par module, le métier

Là où les coding rules disent comment écrire, les AGENT.md disent ce que fait ce module.

Un fichier par domaine fonctionnel

À l'intérieur de chaque module fonctionnel, on met un fichier AGENT.md qui contient les infos technico-fonctionnelles spécifiques au module.

Ces AGENT.md sont écrits par les agents eux-mêmes au moment où ils codent, dans un style optimisé pour un LLM qui doit comprendre vite plutôt que pour un onboarding humain.

Chez Maleus aujourd'hui, on a 97 AGENT.md répartis sur toute l'application, de quelques dizaines à plusieurs centaines de lignes selon la complexité du module.

Structure standard

Le template qu'on a fini par converger, généré et maintenu par les agents:

# <Module> Module

## Overview

[2-4 phrases : raison d'être du module]

## Main Flows

[1 à 3 schémas ASCII des flows principaux]

## Important Files

[Tableau: chemin de fichier | responsabilité]

## Tables Structure

[Tableau par table : colonne | type | description]

## Permissions

[Tableau: API action | rôles autorisés | description]

## Error Codes

[Tableau: code | status HTTP | description]

## Dependencies

[Liste des autres modules / clients dont on dépend]

Toutes les sections ne sont pas dans tous les modules, uniquement celles qui ont du sens. Un module sans BDD n'a pas de section Tables Structure. Un module qui ne consomme pas d'événements AMQP n'a pas de section AMQP Events.

Exemple court : features/agent-sessions/AGENT.md

Pour illustrer, voici un AGENT.md minimal (69 lignes) qui couvre un module de routing d'événements AMQP:

# Agent Sessions Module

## Overview

The Agent Sessions module owns the AMQP event consumption for `snivel.agent-sessions.end` and `snivel.files.change` events. It routes these events to the appropriate feature module (versions or workshops).

This module exists because the `AmqpEventServer` enforces one handler per event pattern, and multiple feature modules need to react to the same events.

## Important Files

| File                                       | Description                                   |
| ------------------------------------------ | --------------------------------------------- |
| `services/AgentSessionsRouting.service.ts` | Event routing logic + Mutex                   |
| `presentation/AgentSessions.consumer.ts`   | AMQP consumer for `snivel.agent-sessions.end` |
| `presentation/FilesChange.consumer.ts`     | AMQP consumer for `snivel.files.change`       |

## Routing Logic

1. Distributed Mutex lock on `agent-session:processing:{conversationId}` prevents duplicate processing
2. If `sessionOrigin` is present, dispatch directly to the owning module (no DB lookups)
3. Fallback: try each handler sequentially until one claims the conversation

Avec 69 lignes comme celles-ci, un agent qui reçoit une tâche du type "ajouter un nouveau type d'événement AMQP" sait immédiatement:

  • que ce module existe pour router des événements AMQP entre features
  • qu'il y a une logique de Mutex à respecter pour éviter le double processing
  • que le fichier à toucher est probablement AgentSessionsRouting.service.ts plus un nouveau consumer

Sans cet AGENT.md, l'agent aurait dû ouvrir 5 à 10 fichiers pour reconstituer cette compréhension.

AGENT.md comme index de la codebase

Un AGENT.md joue le rôle d'index au sens base de données : quand un agent reçoit une tâche, il ouvre ceux des modules potentiellement concernés et, en quelques centaines de tokens par module, il sait:

  • est-ce que ce module est pertinent pour ma tâche ?
  • si oui, quels fichiers je dois ouvrir précisément ?

L'agent ne fouille plus la codebase au hasard, il sait directement quels fichiers ouvrir, ce qui économise des tokens et garde son contexte propre pour le raisonnement.

Benchmark : ce que les AGENT.md font gagner

Pour quantifier, on a joué une question de compréhension de code en production sur le monorepo, "comment les images circulent du frontend studio vers Claude Code dans snivel ?", deux fois, avec et sans accès aux AGENT.md.

MétriqueAvec AGENT.mdSans AGENT.mdΔ
Tours d'agent24×2
Tokens cumulés2,1 M4,3 M+103 %
Coût1,53 $2,28 $+49 %

Même conclusion finale dans les deux cas. Mais sans index, l'agent double sa consommation de tokens et paie 50% de plus juste pour reconstituer ce que les AGENT.md lui auraient dit en quelques lignes.

À l'échelle d'une équipe qui fait tourner des milliers de requêtes par jour, c'est la différence entre une facture API tenable et un budget qui dérape.

La règle qui crée la règle

Et c'est une coding rule qui maintient ces AGENT.md, extrait de backend-architecture.md:

**Module AGENT.md**

Each module MUST contain a `AGENT.md` file at the root.

This file contains a high level description of the module purpose and general behavior. It can contain anything necessary so a Senior developer is happy to read it to understand how to handle the module:

- schema of main flow
- important files
- tables structure
- particular permissions
- important API actions
- important business logic

**You MUST ALWAYS update the AGENT.md file associated with a module after working on it.**

Cette rule est chargée à chaque édition de fichier backend, parce que son paths: est **/{backend,studio-backend}/**/* — un glob générique qui match tous les fichiers du backend. Donc à chaque fois qu'un agent touche un fichier de module, il a en contexte immédiat l'instruction de mettre à jour l'AGENT.md.

La couche 1 (coding rules) maintient ainsi la couche 3 (AGENT.md), sans qu'un humain ait à toucher la doc.

Conclusion

Trois couches, trois moments de chargement, chacune avec un mécanisme de déclenchement adapté.

Sans cette stratification, on plafonne très vite dès que la codebase grossit. On finit par mettre tellement d'instructions au même endroit qu'aucune n'est respectée correctement.

Ça marche parce qu'on respecte le fonctionnement réel de l'attention du LLM : l'info doit être proche du moment de génération, et non pas perdue 20 000 tokens plus haut dans un CLAUDE.md géant.

Les AGENT.md sont eux-mêmes générés et maintenus par les coding agents qui suivent une rule chargée à chaque édition de fichier backend, donc le système se maintient sans intervention humaine sur la documentation.

Chez Maleus, c'est ce qui nous permet de générer la majorité du code en quasi-autonomie sans sacrifier la qualité 😉

Découvrez comment Maleus peut accélérer vos équipes.