Donner de la mémoire à une IA qui n'en a pas
Dans l’article précédent, on a vu que le contexte est roi quand on travaille avec un LLM. Mais il y a un problème fondamental : ces modèles n’ont aucune mémoire persistante. Chaque nouvelle conversation repart de zéro.
Pour contrer ça, il existe des fichiers d’instructions qui sont lus automatiquement par les agents de code à chaque échange. C’est là qu’on peut transmettre nos conventions, notre manière de travailler, et les informations essentielles du projet.
Je vais parler essentiellement de Claude Code, parce que c’est l’outil que j’utilise au quotidien. Mais le principe existe chez les autres agents — Codex a son AGENTS.md, Gemini son GEMINI.md. Le concept est le même, seul le nom du fichier change.
La hiérarchie des fichiers
Claude Code ne lit pas un seul fichier d’instructions, il en lit plusieurs, organisés en hiérarchie du plus global au plus spécifique.
%%{init: {'flowchart': {'curve': 'basis'}}}%%
graph TD
A("<span style='font-size:1.1em'><b>~/.claude/CLAUDE.md</b></span><br/><i style='opacity:0.75'>Global — tous les projets</i>")
B("<span style='font-size:1.1em'><b>./CLAUDE.md</b></span><br/><i style='opacity:0.75'>Projet — commité, partagé</i>")
C("<span style='font-size:1.1em'><b>./CLAUDE.local.md</b></span><br/><i style='opacity:0.75'>Projet local — gitignored</i>")
D("<span style='font-size:1.1em'><b>./src/Entity/CLAUDE.md</b></span><br/><i style='opacity:0.75'>Sous-dossier — chargé à la demande</i>")
E("<span style='font-size:1.1em'><b>.claude/rules/*.md</b></span><br/><i style='opacity:0.75'>Règles modulaires — scopées par chemin</i>")
A --> B --> C --> D --> E
style A fill:transparent,stroke:#10b981,color:#e2e8f0
style B fill:transparent,stroke:#10b981,color:#e2e8f0
style C fill:transparent,stroke:#10b981,color:#e2e8f0
style D fill:transparent,stroke:#10b981,color:#e2e8f0
style E fill:transparent,stroke:#10b981,color:#e2e8f0 Le fichier global (~/.claude/CLAUDE.md)
Ce fichier s’applique à tous vos projets. C’est ici que je mets mes préférences personnelles de travail — les règles qui ne changent pas d’un projet à l’autre.
Le fichier projet (./CLAUDE.md ou ./.claude/CLAUDE.md)
Celui-ci est lu quand vous lancez Claude Code dans le projet. C’est le fichier qu’on commite dans le repo et qu’on partage avec l’équipe.
Les fichiers de sous-dossiers
Si Claude travaille dans un dossier spécifique — disons qu’il modifie une entité dans src/Entity/User.php — et qu’il existe un fichier src/Entity/CLAUDE.md, celui-ci sera chargé également.
C’est utile pour donner des instructions ciblées. Par exemple, dans mes dossiers de tests, je mets des exemples métier prêts à l’emploi pour aider le modèle à créer des cas de test cohérents plus vite.
Les instructions sont cumulatives
Point important : les instructions ne se remplacent pas, elles s’additionnent. Le fichier global est lu en premier, puis le fichier projet, puis les sous-dossiers. Claude reçoit le tout.
Les fichiers .local
Il existe une variante locale : CLAUDE.local.md. Ce fichier se place au même endroit que le CLAUDE.md du projet, mais il n’est pas commité — il va dans le .gitignore.
C’est très utile quand le CLAUDE.md du projet est partagé entre les membres de l’équipe. Chacun peut y ajouter ses instructions personnelles sans polluer le fichier commun.
Les règles modulaires
Pour les projets plus complexes, il existe aussi le dossier .claude/rules/ où l’on peut découper ses instructions en fichiers thématiques. Chaque fichier peut même être scopé à certains chemins du projet via un frontmatter YAML :
---paths: - "src/Api/**/*.php" - "tests/Api/**/*.php"---
- All API endpoints must return JSON responses- Use DTOs for request/response objects- ...Le contenu : moins c’est mieux
C’est probablement le débat le plus récurrent dans la communauté. Les avis divergent, et ils évoluent vite — normal vu le peu de recul qu’on a.
Je vais donc parler de mon expérience.
Rester minimal
Ce qui fait consensus, c’est de garder ces fichiers courts. La documentation officielle recommande de rester sous les 200 lignes par fichier. Et de concentrer les instructions les plus importantes au début du fichier — c’est là qu’elles ont le plus de poids.
Chaque ligne de CLAUDE.md est injectée dans le contexte à chaque message que vous envoyez — pas seulement au début de la conversation. Plus c’est long, plus ça coûte en tokens, et plus les instructions critiques risquent d’être diluées dans le bruit.
La bonne question à se poser : “Si je retire cette ligne, est-ce que Claude va faire des erreurs ?” Si la réponse est non, du coup, virez-la.
Dans le fichier global
Des instructions génériques qui fonctionnent pour tous vos projets. C’est aussi ici que je précise comment j’aime le voir travailler :
# Master Rules
- **NEVER** work directly on main (or master) branch - always create a feature branch- **NEVER** erase or modify previous commits - preserve the complete history, do not amend- **NEVER** use emoji in code (or comment)- **Always** comment in english (even if I prompt in french)- **CRITICAL**: Only modify code directly related to the initial request. Do not make unsolicited changes to unrelated code, even if you notice improvements. Mention issues but do not fix without explicit permission.Dans le fichier projet
Ici, je décris le projet : ce que c’est, la stack utilisée, les commandes essentielles, et les conventions spécifiques.
Si vous avez de la documentation dans votre codebase, je recommande de créer un fichier d’index qui résume en quelques mots chaque document et le lien pour y accéder. Le CLAUDE.md peut alors pointer vers cet index avec la syntaxe d’import @docs/index.md. Le modèle ne sera pas surchargé au démarrage, et quand il aura besoin d’un document spécifique, il pourra aller le charger à la demande.
# Mon Projet
Application de gestion de commandes. Symfony 7, API Platform 4, PostgreSQL.
## Commandes
- `make dev` : lancer l'environnement de dev- `make test` : lancer les tests- `make lint` : vérifier le code
## Documentation
@docs/index.mdÉviter le /init
Je ne recommande pas d’utiliser la commande /init pour générer le fichier CLAUDE.md. Elle a tendance à y mettre beaucoup de bruit — des descriptions de la structure du projet, des listes de fichiers, des conventions que Claude peut deviner tout seul en lisant le code. La première fois que je l’ai lancé, le fichier généré faisait 180 lignes. J’en ai gardé 12.
D’ailleurs, un benchmark récent de TechLoom a comparé les résultats entre un CLAUDE.md bien rempli et un prompt identique avec ce fichier vide, sur 1 188 runs et 10 profils d’instructions différents. Le résultat est frappant : le profil vide a obtenu le meilleur score, avec un écart total de seulement 1,44 points sur 100 entre le meilleur et le pire profil.
Une étude de l’ETH Zurich a testé quatre agents de code (dont Claude Code) sur 438 tâches. Leur conclusion : les fichiers de contexte écrits par des développeurs n’apportent qu’un gain marginal sur les performances, tout en augmentant le coût de plus de 20%.
Je n’ai pas reproduit ces benchmarks moi-même, mais ça rejoint mon ressenti : sur les projets où je travaille, il y a finalement très peu d’instructions à vraie forte valeur ajoutée. Au final, l’essentiel c’est d’avoir les quelques règles qui comptent — pas un roman.
Quand CLAUDE.md ne suffit pas
C’est un point crucial que j’ai mis du temps à intégrer : les instructions dans CLAUDE.md seront toujours sujettes à interprétation. Elles ne sont jamais déterministes.
Ce que j’entends par là : je ne veux jamais que le modèle pousse du code sur le repo distant de lui-même, ni qu’il accède à mes bases de données via le client MySQL — de peur de manipuler des données de production par accident. Aussi bien formulée soit-elle, une instruction dans CLAUDE.md reste une suggestion que le modèle peut choisir d’ignorer.
Pour ces interdictions critiques, il faut utiliser des mécanismes déterministes.
Les permissions
Le système de permissions de Claude Code permet de bloquer ou autoriser explicitement certaines commandes :
{ "permissions": { "deny": [ "Bash(git push *)", "Bash(gh pr merge *)", "Bash(mysql *)", "Bash(mysqldump *)", "Bash(mysqladmin *)", "Bash(mysqlsh *)" ] }}Ici, peu importe ce que dit le CLAUDE.md : Claude ne pourra physiquement pas exécuter ces commandes. C’est un verrou, pas une suggestion.
Attention cependant : les permissions font du pattern matching sur la commande complète. Bash(git push *) bloque git push origin main, mais pas cd /repo && git push origin main. Pour attraper les variantes, il faut aller plus loin.
Les hooks
Pour aller encore plus loin, les hooks permettent d’exécuter du code personnalisé à des moments clés du cycle de vie de Claude Code. Le hook PreToolUse se déclenche avant chaque exécution d’outil — c’est l’endroit idéal pour bloquer des actions dangereuses.
Voici un exemple concret. Sur ma machine, j’ai des proxys Cloud SQL configurés qui pointent vers des bases de données de production. Je veux m’assurer que Claude ne puisse jamais y accéder, même par accident :
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "bash ~/.claude/hooks/block-database-access.sh" } ] } ] }}Et le script qui fait le travail :
#!/bin/bashINPUT=$(cat)CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""' 2>/dev/null)
# Extract the first token (the binary being invoked)FIRST_TOKEN=$(echo "$CMD" | sed 's/^[[:space:]]*//' | awk '{print $1}')
# Block MySQL CLI toolsDB_TOOLS="mysql|mysqldump|mysqladmin|mysqlsh|mycli"if echo "$FIRST_TOKEN" | grep -qEi "^($DB_TOOLS)$"; then echo "BLOCKED: Direct database CLI access is forbidden." >&2 exit 2 # exit code 2 = block the actionfi
# Block cloud-sql-proxy socket accessif echo "$CMD" | grep -qF '/path/to/cloud-sql-sockets'; then echo "BLOCKED: Access to production database sockets is forbidden." >&2 exit 2fiLe code de sortie 2 est la clé : il bloque l’action et renvoie le message d’erreur à Claude comme feedback. Du coup le modèle comprend qu’il n’a pas le droit et adapte son approche.
C’est de la défense en profondeur : les permissions bloquent les commandes connues, le hook attrape les variantes et les accès indirects.
Et pour que Claude puisse quand même accéder à la base de données de développement locale ? J’utilise un serveur MCP custom qui expose uniquement la base de dev via des outils typés et sécurisés. Mais ça, ce sera le sujet d’un prochain article.
Le système de mémoire automatique
Jusqu’ici, on parle de fichiers que vous écrivez pour donner des instructions à Claude. Mais depuis la version 2.1.59, Claude Code peut aussi apprendre par lui-même grâce au système de mémoire automatique.
Comment ça fonctionne
Quand vous travaillez sur un projet, Claude observe et retient certaines informations : les commandes de build qui fonctionnent, les patterns architecturaux, les décisions techniques, vos préférences de style. Il enregistre ces apprentissages dans un fichier MEMORY.md, stocké par projet dans ~/.claude/projects/<projet>/memory/.
Les 200 premières lignes de ce fichier sont automatiquement injectées dans le contexte au démarrage de chaque conversation. Claude ne sauvegarde pas quelque chose à chaque session — il le fait de manière sélective, quand il estime que l’information sera utile dans de futures conversations.
La différence avec CLAUDE.md
| CLAUDE.md | MEMORY.md | |
|---|---|---|
| Qui écrit | Vous | Claude |
| Contenu | Instructions, règles, conventions | Apprentissages, patterns observés |
| Portée | Partageable (commité dans git) | Personnel (local à votre machine) |
| Contrôle | Total | Supervisé (vous pouvez éditer/supprimer) |
On peut aussi demander explicitement à Claude de mémoriser quelque chose (“souviens-toi que…”) ou de consulter sa mémoire. Et on peut désactiver la fonctionnalité avec /memory en session ou via la variable d’environnement CLAUDE_CODE_DISABLE_AUTO_MEMORY.
C’est une couche complémentaire intéressante. Le CLAUDE.md donne le cadre, la mémoire automatique capture les détails opérationnels qui s’accumulent au fil du travail. Ensemble, ils permettent à un agent sans mémoire native de retrouver un semblant de continuité entre les sessions.
Ce que j’en retiens
Mes CLAUDE.md — global et projet — font aujourd’hui plusieurs dizaines de lignes chacun. Ça s’est construit au fil des erreurs, des oublis et des corrections. Le piège, ce n’est pas d’en écrire trop, c’est d’en écrire à l’avance. Les meilleures règles sont celles qu’on ajoute après avoir vu le modèle faire une bêtise.
Mais donner du contexte et poser des garde-fous, c’est seulement la moitié du chemin. Il reste un gros morceau : donner à l’agent les moyens d’agir sur l’environnement projet — lancer les tests, interroger la base de dev, exécuter des commandes métier. C’est ce qu’on verra dans le prochain article.