Free

Un buon CLAUDE.md non descrive le funzionalità — cattura solo ciò che Claude non può vedere leggendo il codice

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.

Descrivere funzionalità è spreco di token

Chi scrive CLAUDE.md per la prima volta spesso lo tratta come un README — descrivendo ogni funzionalità chiave:

  • "Il sistema di giuria permette agli utenti di segnalare contenuti; il segnalato va a voto pubblico, i giurati sono estratti da..."
  • "I pagamenti x402 innescano trasferimenti on-chain tramite il codice di stato HTTP 402..."
  • "I like danno punti; a 400 punti l'utente diventa VIP..."

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.

Le 6 categorie che davvero salvano la giornata

1. Scelte architetturali controintuitive

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:

  • Gli asset passano per Sprockets (inerzia dei progetti vecchi)
  • JS usa Webpacker o esbuild
  • Il frontend è React, o una mescolanza Stimulus + Turbo
  • Il rich text è ActionText vanilla

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.

2. Ripartizione multi-database

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.

3. Le "convenzioni invisibili" del 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

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.

4. Soglie di business hardcoded

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".

5. Cartello stradale dello stack auth/authz

- 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à:

  • Scrive unless current_user.admin? direttamente in un controller
  • Tira fuori un residuo di CanCan non più in uso
  • Inventa un suo metodo authorize? nel modello

Con "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.

6. Vincoli esterni a livello di progetto

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

Aggiungendo una nuova funzionalità, i default di Claude sono:

  • Aggiungere solo stringhe in inglese
  • Scrivere test con Minitest + fixtures (default Rails)

Ma il tuo progetto in realtà richiede:

  • Traduzioni in 3 locale
  • RSpec + FactoryBot, non fixtures

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".

L'altro lato: questi sono puro spreco

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.

Audit sul campo: anche il mio CLAUDE.md si può dimezzare

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:

  • Numeri sepolti nel sistema di giuria (soglia di voto, requisiti di eleggibilità del giurato) — per nulla esposti nel CLAUDE.md
  • Se x402 ha chain id, indirizzo del contratto, env var obbligatorie — Claude non andrà a grep nel file di config e inventerà valori
  • Regole di vincolo speciali nei service di trading di punti / Referral

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.

5 domande prima di inserire una regola

Ogni volta che voglio aggiungere qualcosa al CLAUDE.md, passo attraverso la seguente checklist:

  1. Claude può dedurre questa regola leggendo 3 file? Se sì — non scriverla, lascia che legga.
  2. Questa regola è controintuitiva? (Soglie insolite, scelte di libreria fuori mainstream, config che contraddice i default upstream.) Se sì — va scritta obbligatoriamente.
  3. È un'invariante a livello di codebase, o influenza un solo file? Se un solo file — commento di codice, non sale nel CLAUDE.md.
  4. Violare la regola farà sbagliare Claude in silenzio? (Nessun errore, ma semantica sbagliata — DB errato, traduzione mancante, policy saltata.) Se sì — obbligatoria.
  5. Si spiega in 3 righe? Se no — tu stesso non l'hai ancora chiarita. Non scriverla ancora.

Le regole che passano tutte e 5 restano; una che non passa si cancella o si riscrive.

Riassunto in una riga

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.