Un buon CLAUDE.md non è un README — cattura invarianti che Claude non può dedurre dal codice. 6 cose da scrivere, 4 da evitare, 5 domande.
Il mio progetto Pickful ha un sistema di giuria di comunità decentralizzata, pagamenti in cripto con x402, Sign-In with Ethereum, multi-database, push in tempo reale — tutti stack usciti negli ultimi uno o due anni. Claude consegna queste funzionalità velocemente e pulite.
Ma apri il CLAUDE.md del progetto e noterai: sistema di giuria e x402 — non compaiono, nemmeno una volta.
Non è una dimenticanza. Lo scopo del CLAUDE.md non è mai stato "descrivere le funzionalità". È catturare quelle cose che Claude non riuscirà mai a dedurre leggendo il codice.
Chi scrive CLAUDE.md per la prima volta spesso lo tratta come un README — descrivendo ogni funzionalità chiave:
Questi contenuti Claude li apre su topic_review_service.rb / x402.rb / like_points_service.rb e li legge più accuratamente di come tu li scriva. Mille parole di descrizione di business logic costano a Claude qualche centinaio di token da leggere direttamente dal codice — senza deriva interpretativa. Il codice è il fatto. Le descrizioni sono informazioni di seconda mano.
Ciò che fa davvero inciampare Claude sono queste 6 categorie.
Il CLAUDE.md di Pickful ha righe come queste:
Propshaft (not Sprockets)
ImportMap (no JavaScript bundler)
Hotwire: Turbo Frames, Turbo Streams, Stimulus
Lexxy gem overrides ActionText:
config.lexxy.override_action_text_defaults = false
Ogni riga va contro l'ipotesi di default. Visto un progetto Rails, le ipotesi di default di Claude sono:
Senza queste righe nel CLAUDE.md, chiedi a Claude di aggiungere una nuova funzionalità JS: alta probabilità che installi Webpacker, modifichi package.json, scriva la config del bundler — tutto sbagliato, e sbagliato in modo silenzioso (l'app gira, ma la asset pipeline è contaminata).
Queste righe nel CLAUDE.md dicono a Claude: non indovinare, è già deciso.
PostgreSQL with 4 separate databases:
- primary - Main application data
- cache - Solid Cache storage
- queue - Solid Queue jobs
- cable - Action Cable subscriptions
Scrittura sobria, ma ti può risparmiare una notte intera. Il multi-DB di default di Rails 8 è un comportamento nuovo — Claude non va a controllare autonomamente quante DB usi. Una migration apparentemente innocua atterra nella DB sbagliata e in sviluppo non dà errore (tutte e quattro sono PostgreSQL, lo schema migra ovunque). Ma in produzione la tabella dei job di Solid Queue si infila nel backup della primary, o un modello di primary fa query sulla DB di cache — bug che prendono giorni per emergere.
Due righe nel CLAUDE.md vs. una giornata di debug in produzione.
/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
Le rotte Claude le vede in routes.rb, ma le convenzioni di lunghezza (4-5 caratteri, 3-4 caratteri) sono sepolte nella logica di generazione degli slug nei modelli o nei service. Chiedi a Claude un nuovo tipo di short link: probabile che generi uno slug a 6 caratteri, stile UUID o solo numerico — fuori passo con il linguaggio visivo dell'intero sistema.
Il tratto di queste "convenzioni": violarle non produce errore, ma chi legge il codice dopo avvertirà qualcosa di stonato. Va scritta.
VIP status at 400+ points
Posts with 15+ likes are "hot" posts
Entrambi i numeri vivono da qualche parte nel codice (User#vip?, scope Post#hot?). Il problema: quando Claude tocca qualcosa di adiacente — regolare le ricompense di punti, aggiungere una notifica "quasi VIP", scrivere un cron per fissare gli hot post — non allinea automaticamente le soglie in altri posti.
Risultato: premi un task con 500 punti ma il copy dice "puoi diventare VIP" (bastano 400); o fai seed data per una nuova funzionalità con pochi like e non si supera mai la soglia 15.
Claude ha capacità di coding forti, ma non ha senso numerico di tutto il sistema. Mettere le soglie chiave nel CLAUDE.md fa sì che ogni conversazione inizi sapendo che "400 e 15 sono numeri speciali".
- Devise (authentication) + Pundit (authorization)
- Pundit policies in app/policies/
- Check UserPolicy, PostPolicy, etc. for permission rules
Il ruolo di questa riga è navigazione, non descrizione.
Senza, quando Claude deve aggiungere un nuovo controllo di permessi, tre possibilità:
unless current_user.admin? direttamente in un controllerauthorize? nel modelloCon "Pundit policies in app/policies/" scritto, Claude ogni volta va direttamente in app/policies/ ad aggiungere un file policy — stile uniforme.
Una riga elimina il "lavoro da detective" di Claude ogni volta.
Supported locales: en, zh-CN, zh-TW
Testing stack: RSpec + FactoryBot + Capybara + Shoulda Matchers
Aggiungendo una nuova funzionalità, i default di Claude sono:
Ma il tuo progetto in realtà richiede:
Violare questi "vincoli esterni a livello di progetto" genera tanto lavoro postumo — traduzioni da completare, test da riscrivere. Scriverlo nel CLAUDE.md significa inchiodare una volta per tutte "le cose che vanno fatte ogni volta".
Tanto importante quanto "da scrivere obbligatoriamente" è "non scrivere". Le categorie seguenti, vedi = cancelli:
1. Descrizioni di funzionalità
"Sistema di giuria: gli utenti possono segnalare contenuti non conformi, il segnalato entra in votazione pubblica, i giurati sono scelti da..."
→ Claude apre topic_review_service.rb e lo legge più accuratamente del tuo testo. Mettere questo in ogni nuova conversazione è puro spreco.
2. Ciò che si legge immediatamente dall'albero delle cartelle / Gemfile
"app/models/ contiene i modelli ActiveRecord", "Usa Rails 8", "DB PostgreSQL"
→ Claude dà un'occhiata alla root del progetto e al Gemfile, sa.
3. Conoscenza di programmazione generica
"Controllers should be thin, delegate to services", "Evita gli N+1", "Scrivi test per le funzionalità principali"
→ È già nei dati di training di Claude. Scrivilo solo se il tuo progetto è anomalo — per esempio "deliberatamente non usiamo il service layer; la logica vive nei controller".
4. Contesto del task corrente
"In questo momento stiamo refactorizzando il sistema di pagamenti; il focus è..."
→ Questo è contesto di conversazione, non fatto del progetto. Metterlo nel CLAUDE.md inquina tutte le altre conversazioni.
Metti la prova di "ce la posso fare" prima del sermone. Dopo aver scritto la sezione sopra, ho passato il mio CLAUDE.md di Pickful — 238 righe — attraverso le 5 domande. Risultato: circa metà è spreco.
Da tagliare (~120 righe):
La maggior parte del blocco di comandi di sviluppo (70 righe → 10): bin/setup / bin/rails db:migrate / bundle exec rspec / bin/rubocop / bin/brakeman sono tutti comandi Rails standard, già nel training di Claude. Si tengono solo i tre specifici del progetto: bin/jobs (worker di Solid Queue), bin/importmap pin (specifico per ImportMap), bin/kamal deploy.
Lista Core Domain Models (35 righe → cancellare tutto): elencare 20 modelli con il loro ruolo è l'esempio classico di "CLAUDE.md stile README" — Claude lancia ls app/models/ o legge un file di modello, e sa. Metterlo in ogni conversazione è puro spreco.
Voci standard in Tech Stack (28 righe → 8): Rails 8 / Devise / Pundit / Tailwind / pg_search sono tutti leggibili immediatamente dal Gemfile. Tenere solo le controintuitive: Propshaft / ImportMap / Lexxy / x402-rails / Grover.
Conoscenza generica di programmazione sparsa: "Controllers should be thin", "Use app/jobs/ for async processing", cosa testano request specs vs. model specs — Claude lo fa di default. Solo spreco di token.
Ciò che resta, ~100 righe: convenzioni di routing URL, ripartizione 4 DB, soglie VIP 400 / Hot 15, override Lexxy, scelte architetturali anti-default, cartello Pundit, lista locale, FactoryBot (non fixtures).
Ma ancora più importante: quali invarianti mancano?
Durante l'audit ho realizzato alcune cose che avrei dovuto aggiungere e non ho mai aggiunto:
238 righe compresse a 100–120, più 5–10 righe di invarianti prima omesse — è più vicino alla "densità giusta" di un CLAUDE.md.
Questo non è un tutorial. È un audit al mio stesso progetto — e anch'io ho sbagliato. La forma corretta del CLAUDE.md è continuare a cancellare e continuare ad aggiungere — qualsiasi progetto che matura un poco dovrebbe avere un CLAUDE.md sempre più corto e più denso nel tempo.
Ogni volta che voglio aggiungere qualcosa al CLAUDE.md, passo attraverso la seguente checklist:
Le regole che passano tutte e 5 restano; una che non passa si cancella o si riscrive.
L'uso corretto del CLAUDE.md non è "presentare il progetto", ma comprimere il sapere tacito tra te e la codebase che Claude non potrà mai colmare leggendo codice — scelte insolite, soglie invisibili, convenzioni contro i default, vincoli esterni a livello di progetto.
Ogni riga che aggiungi deve rispondere a una domanda: "Questa cosa, Claude non può leggerla dal codice?" Non può — resta. Può — via.
Un CLAUDE.md scritto così è di solito più corto di oltre metà della bozza iniziale, ma vale molto di più per conversazione di migliaia di parole di descrizione di funzionalità. E resta sempre "non ancora finito" — ogni nuova funzionalità spedita fa emergere un altro invariante che doveva esserci, e un paragrafo che ora si può tagliare. Cancella e aggiungi, cancella e aggiungi — è la manutenzione quotidiana del CLAUDE.md.