Free

Un bon CLAUDE.md ne décrit pas les fonctionnalités — il ne capture que ce que Claude ne peut pas voir en lisant le code

Un bon CLAUDE.md n'est pas un README — il capture les invariants que Claude ne peut pas déduire du code. 6 à écrire, 4 à éviter, 5 questions.


Mon projet Pickful contient un système de jury communautaire décentralisé, des paiements crypto x402, Sign-In with Ethereum, une configuration multi-base, du push en temps réel — que des stacks sortis ces deux dernières années. Claude livre ces fonctionnalités vite et proprement.

Mais ouvre le CLAUDE.md du projet et tu verras : système de jury et x402 n'y apparaissent pas — pas une seule fois.

Ce n'est pas un oubli. Le rôle du CLAUDE.md n'a jamais été de « décrire les fonctionnalités ». C'est de capturer ce que Claude ne pourra jamais déduire en lisant le code.

Les descriptions de fonctionnalités sont des tokens gâchés

Les gens qui écrivent un CLAUDE.md pour la première fois le traitent souvent comme un README — en décrivant chaque fonctionnalité principale :

  • « Le système de jury permet aux utilisateurs de signaler du contenu ; le contenu signalé passe à un vote public où les jurés... »
  • « Les paiements x402 déclenchent des transferts on-chain via le code de statut HTTP 402... »
  • « Les likes donnent des points ; à 400 points l'utilisateur devient VIP... »

Claude ouvre topic_review_service.rb / x402.rb / like_points_service.rb et le lit plus précisément que tu ne l'écris jamais. Une description de mille mots sur la logique métier coûte à Claude quelques centaines de tokens à lire depuis le code — et sans dérive d'interprétation. Le code est le fait. Les descriptions, une information de seconde main.

Ce qui fait réellement trébucher Claude, ce sont ces 6 catégories.

Les 6 catégories qui sauvent vraiment

1. Choix d'architecture contre-intuitifs

Le CLAUDE.md de Pickful contient ce genre de lignes :

Propshaft (not Sprockets)
ImportMap (no JavaScript bundler)
Hotwire: Turbo Frames, Turbo Streams, Stimulus
Lexxy gem overrides ActionText:
  config.lexxy.override_action_text_defaults = false

Chaque ligne va à l'encontre du pari par défaut. Face à un projet Rails, les suppositions par défaut de Claude sont :

  • Les assets passent par Sprockets (l'inertie des vieux projets)
  • JS utilise Webpacker ou esbuild
  • Le front est React, ou un mélange Stimulus + Turbo
  • Le rich text, c'est ActionText tel quel

Sans ces lignes dans CLAUDE.md, demande à Claude d'ajouter une nouvelle fonctionnalité JS : forte probabilité qu'il installe Webpacker, modifie package.json, écrive une config bundler — tout faux, et faux en silence (l'app tourne, mais le pipeline d'assets est pollué).

Ces lignes dans CLAUDE.md disent à Claude : ne devine pas, c'est décidé.

2. Répartition multi-base

PostgreSQL with 4 separate databases:
- primary - Main application data
- cache   - Solid Cache storage
- queue   - Solid Queue jobs
- cable   - Action Cable subscriptions

Formulation sobre, mais ça peut t'économiser une nuit entière. Le multi-DB par défaut de Rails 8 est un comportement nouveau — Claude ne va pas vérifier de lui-même combien tu en utilises. Une migration apparemment anodine qui tombe dans la mauvaise base ne lève pas d'erreur en dev (les quatre sont en PostgreSQL, le schema passe partout). Mais en prod, la table des jobs de Solid Queue se glisse dans le backup de primary, ou un modèle de primary requête la base de cache — des bugs qui mettent des jours à remonter.

Deux lignes dans CLAUDE.md contre une journée de debug en prod.

3. Les « conventions invisibles » du routing URL

/p-{slug} - Short post URLs (4-5 char alphanumeric)
/t-{slug} - Topic URLs (3-4 char alphanumeric)
/s-{code} - Short URL redirects (3-4 char alphanumeric)
/r-{referral} - Referral links

Les routes, Claude peut les voir dans routes.rb. Mais les conventions de longueur (4-5 caractères, 3-4 caractères) sont enfouies dans la logique de génération de slug, côté modèle ou service. Demande à Claude un nouveau type de short link, il générera probablement un slug de 6 caractères, style UUID ou purement numérique — en décalage avec le langage visuel du système.

Caractéristique de ces « conventions » : les enfreindre ne provoque pas d'erreur, mais le code paraît bancal au prochain lecteur. À écrire, obligatoirement.

4. Seuils métier codés en dur

VIP status at 400+ points
Posts with 15+ likes are "hot" posts

Les deux nombres vivent quelque part dans le code (User#vip?, scope Post#hot?). Le problème : quand Claude touche à quelque chose de proche — ajuster des récompenses, ajouter une notification « presque VIP », écrire un cron qui épingle les hot posts — il n'aligne pas automatiquement les seuils ailleurs.

Résultat : tu récompenses une tâche par 500 points mais le wording dit « tu peux devenir VIP » (400 suffit) ; ou tu alimentes des seed data pour une nouvelle fonctionnalité avec trop peu de likes, le seuil 15 n'est jamais franchi.

Claude a une belle maîtrise du code, mais pas de sens numérique à l'échelle du système. Mettre les seuils clés dans CLAUDE.md fait que chaque conversation démarre en sachant que « 400 et 15 sont des nombres spéciaux ».

5. Panneau indicateur de la stack auth/authz

- Devise (authentication) + Pundit (authorization)
- Pundit policies in app/policies/
- Check UserPolicy, PostPolicy, etc. for permission rules

Le rôle de cette ligne est la navigation, pas la description.

Sans elle, quand Claude doit ajouter un nouveau contrôle de permission, trois possibilités :

  • Il écrit unless current_user.admin? en dur dans un controller
  • Il déterre un vestige de CanCan plus utilisé
  • Il invente une méthode authorize? dans un modèle

Avec « Pundit policies in app/policies/ » écrit, Claude va à chaque fois ajouter un fichier policy dans app/policies/ — style cohérent.

Une ligne supprime le « travail de détective » de Claude à chaque fois.

6. Contraintes externes à l'échelle du projet

Supported locales: en, zh-CN, zh-TW
Testing stack: RSpec + FactoryBot + Capybara + Shoulda Matchers

En ajoutant une nouvelle fonctionnalité, les defaults de Claude sont :

  • Ajouter uniquement les chaînes anglaises
  • Écrire les tests en Minitest + fixtures (default Rails)

Alors que ton projet a besoin en vrai :

  • De traductions dans 3 locales
  • RSpec + FactoryBot, pas fixtures

Enfreindre ces « contraintes externes à l'échelle du projet » génère un tas de travail d'après — traductions à compléter, tests à réécrire. Écrire dans CLAUDE.md, c'est clouer une fois pour toutes « ce qu'il faut faire à chaque fois ».

L'autre versant : là c'est du pur gâchis

Aussi important que « à écrire absolument » : « ne pas écrire ». Les catégories ci-dessous, tu vois = tu supprimes :

1. Descriptions de fonctionnalités

« Système de jury : les utilisateurs peuvent signaler du contenu non conforme, le signalé passe à un vote public, les jurés sont choisis parmi... »

→ Claude ouvre topic_review_service.rb et le lit plus précisément que tu ne l'as écrit. Balancer ça dans chaque nouvelle conversation est du gâchis pur.

2. Ce qui se lit instantanément dans l'arborescence / le Gemfile

« app/models/ contient des modèles ActiveRecord », « Utilise Rails 8 », « BD PostgreSQL »

→ Claude jette un œil à la racine et au Gemfile, il sait.

3. Savoir de programmation général

« Controllers should be thin, delegate to services », « Éviter les N+1 », « Écrire des tests pour les fonctionnalités principales »

→ C'est déjà dans les données d'entraînement de Claude. N'écris ça que si ton projet est atypique — par exemple « volontairement, on n'utilise pas de service layer ; la logique vit dans les controllers ».

4. Contexte de la tâche en cours

« On est en train de refactorer le système de paiement, le focus est... »

→ Ça, c'est du contexte de conversation, pas un fait du projet. Le glisser dans CLAUDE.md pollue toutes les autres conversations.

Audit réel : mon propre CLAUDE.md peut aussi perdre la moitié

Placer la preuve « je peux le faire » avant le discours. Après avoir écrit la section précédente, j'ai repassé mon propre CLAUDE.md de Pickful — 238 lignes — par les 5 questions. Résultat : environ la moitié est du gâchis.

À couper (~120 lignes):

La majorité du bloc de commandes de dev (70 lignes → 10) : bin/setup / bin/rails db:migrate / bundle exec rspec / bin/rubocop / bin/brakeman sont toutes des commandes Rails standard, déjà dans l'entraînement de Claude. On garde seulement les trois spécifiques au projet : bin/jobs (worker Solid Queue), bin/importmap pin (propre à ImportMap), bin/kamal deploy.

Liste Core Domain Models (35 lignes → virer intégralement) : lister 20 modèles avec leurs rôles, c'est l'exemple type du « CLAUDE.md façon README » — Claude lance ls app/models/ ou lit un fichier modèle et sait. Balancer ça dans chaque conversation est du gâchis pur.

Éléments standards dans Tech Stack (28 lignes → 8) : Rails 8 / Devise / Pundit / Tailwind / pg_search se lisent tous immédiatement depuis le Gemfile. On garde uniquement les contre-intuitifs : Propshaft / ImportMap / Lexxy / x402-rails / Grover.

Quelques lignes de savoir générique dispersées : « Controllers should be thin », « Use app/jobs/ for async processing », ce que testent request specs vs. model specs — Claude fait ça par défaut. Des tokens brûlés.

Ce qu'il reste, ~100 lignes : conventions de routing URL, répartition des 4 DB, seuils VIP 400 / Hot 15, override Lexxy, choix d'archi anti-défaut, panneau Pundit, liste des locales, FactoryBot (pas fixtures).

Mais encore plus important : quels invariants manquent ?

Pendant l'audit j'ai réalisé quelques trucs que j'aurais dû ajouter et que je n'ai jamais ajoutés :

  • Les nombres enfouis dans le système de jury (seuil de vote, critères d'éligibilité du juré) — jamais exposés dans CLAUDE.md
  • Si x402 a un chain id, une adresse de contrat, des env vars obligatoires — Claude ne va pas greper le fichier de config, il inventera des valeurs
  • Les règles contraignantes des services de trading de points / Referral

238 lignes compressées à 100–120, plus 5–10 lignes d'invariants précédemment omis — ça se rapproche de la « bonne densité » d'un CLAUDE.md.

Ceci n'est pas un tutoriel. C'est un audit de mon propre projet — et je me suis trompé aussi. La forme correcte d'un CLAUDE.md, c'est continuer à supprimer et à ajouter — tout projet qui mûrit un peu devrait avoir un CLAUDE.md plus court et plus dense avec le temps.

5 questions avant d'inscrire une règle

Chaque fois que je veux ajouter quelque chose dans CLAUDE.md, je passe par la checklist suivante :

  1. Claude peut-il déduire cette règle en lisant 3 fichiers ? Si oui — ne l'écris pas, laisse-le lire.
  2. Cette règle est-elle contre-intuitive ? (Seuils inhabituels, choix de librairie hors du mainstream, config à l'opposé des defaults upstream.) Si oui — à écrire obligatoirement.
  3. Est-ce un invariant à l'échelle de la codebase, ou ça n'affecte qu'un fichier ? Les règles limitées à un fichier se mettent en commentaire de code, pas dans CLAUDE.md.
  4. Enfreindre la règle fera-t-il que Claude se trompe en silence ? (Sans erreur, mais sémantique fausse — mauvaise DB, traduction oubliée, policy zappée.) Si oui — obligatoire.
  5. Est-ce que ça tient en 3 lignes ? Si non — c'est que toi-même tu n'as pas encore clairement digéré. Ne l'écris pas encore.

Les règles qui passent les 5 restent ; celles qui en ratent une, on supprime ou on réécrit.

Résumé en une ligne

Le bon usage du CLAUDE.md, ce n'est pas « présenter le projet », c'est compresser le savoir tacite entre toi et la codebase que Claude ne comblera jamais en lisant du code — choix inhabituels, seuils invisibles, conventions contraires aux defaults, contraintes externes à l'échelle du projet.

Chaque ligne que tu ajoutes doit répondre à : « Ça, est-ce que Claude ne peut pas le lire dans le code ? » Non — ça reste. Oui — ça dégage.

Un CLAUDE.md écrit de cette façon est généralement plus de moitié plus court que la version initiale, mais sa valeur par conversation dépasse de loin des milliers de mots de description de fonctionnalités. Et il n'est jamais « fini » — chaque nouvelle fonctionnalité que tu livres révèle un autre invariant qui aurait dû s'y trouver, et un paragraphe qui peut désormais partir. Supprimer et ajouter, supprimer et ajouter — c'est la maintenance quotidienne du CLAUDE.md.