Free

Um bom CLAUDE.md não descreve features — só captura o que o Claude não consegue ver lendo o código

Um bom CLAUDE.md não é um README — captura invariantes que o Claude não consegue inferir do código. 6 coisas para escrever, 4 para pular, 5 perguntas.


Meu projeto Pickful tem sistema de júri comunitário descentralizado, pagamentos em cripto com x402, Sign-In with Ethereum, multi-banco de dados, push em tempo real — todas stacks que surgiram nos últimos um ou dois anos. O Claude entrega essas features rápido e bem.

Mas abre o CLAUDE.md do projeto e você vai perceber: sistema de júri e x402 — essas palavras não aparecem, nem uma vez.

Isso não é descuido. O propósito do CLAUDE.md nunca foi "descrever features". É capturar aquilo que o Claude jamais vai deduzir só lendo o código.

Descrever features é puro desperdício de tokens

Quem escreve CLAUDE.md pela primeira vez costuma tratá-lo como README — descrevendo cada feature principal:

  • "O sistema de júri permite que usuários denunciem conteúdo; o que é denunciado vai pra votação pública, onde jurados são sorteados de..."
  • "Os pagamentos x402 disparam transferências on-chain via código de status HTTP 402..."
  • "Curtidas dão pontos; com 400 pontos o usuário vira VIP..."

Conteúdo assim o Claude abre topic_review_service.rb / x402.rb / like_points_service.rb e lê com mais precisão do que você escreve. Mil palavras de lógica de negócio custam algumas centenas de tokens pro Claude ler direto do código — sem desvio interpretativo. Código é fato. Descrição é informação de segunda mão.

O que realmente faz o Claude tropeçar são estas 6 categorias.

As 6 categorias que de fato salvam o jogo

1. Escolhas arquiteturais contra-intuitivas

O CLAUDE.md do Pickful tem linhas assim:

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

Cada linha vai contra o palpite default. Vendo um projeto Rails, os palpites default do Claude são:

  • Assets via Sprockets (inércia de projetos antigos)
  • JS com Webpacker ou esbuild
  • Frontend é React, ou mistura de Stimulus + Turbo
  • Rich text é ActionText puro

Sem essas linhas no CLAUDE.md, peça ao Claude pra adicionar uma feature JS nova: alta chance de ele instalar Webpacker, mexer em package.json, escrever config de bundler — tudo errado, e errado de forma silenciosa (o app roda, mas o pipeline de assets ficou contaminado).

Essas linhas no CLAUDE.md dizem pro Claude: não adivinhe, já foi decidido.

2. Divisão entre múltiplos bancos

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

Redação simples, mas pode economizar uma noite inteira. Multi-DB default do Rails 8 é um comportamento novo, o Claude não vai por conta própria conferir quantos bancos você usa. Uma migration que parece inocente pousa no banco errado e em dev não dá erro (os quatro são PostgreSQL, o schema migra em qualquer um). Só que em produção a tabela de jobs do Solid Queue vaza pro backup do primary, ou um modelo do primary consulta o banco de cache — bugs que levam dias pra emergir.

Duas linhas no CLAUDE.md vs. um dia caçando bug em produção.

3. As "convenções invisíveis" do roteamento de 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

As rotas o Claude enxerga em routes.rb, mas as convenções de comprimento (4-5 chars, 3-4 chars) estão enterradas na lógica de geração de slug em modelos ou services. Peça ao Claude um novo tipo de short link e é provável que ele gere slug de 6 caracteres, estilo UUID, ou só dígitos — em descompasso com a linguagem visual do sistema todo.

O traço dessas "convenções": violá-las não produz erro, mas o código soa estranho pro próximo leitor. Precisa escrever.

4. Thresholds de negócio hardcoded

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

Os dois números vivem em algum lugar do código (User#vip?, scope Post#hot?). O problema: quando o Claude mexe em algo adjacente — ajustar recompensas, adicionar notificação "quase VIP", escrever cron pra fixar hot posts — ele não alinha automaticamente os thresholds em outros lugares.

Resultado: você recompensa uma tarefa com 500 pontos mas a copy diz "você pode virar VIP" (com 400 já basta); ou faz seed de uma feature nova com poucas curtidas e nada cruza o threshold de 15.

A capacidade de código do Claude é forte, mas ele não tem senso numérico do sistema inteiro. Pondo thresholds-chave no CLAUDE.md, toda conversa começa sabendo que "400 e 15 são números especiais".

5. Placa de sinalização do stack de auth/authz

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

O papel desta linha é navegação, não descrição.

Sem ela, quando o Claude precisa adicionar um novo controle de permissão, três possibilidades:

  • Escreve unless current_user.admin? direto no controller
  • Desenterra um resquício de CanCan que não é mais usado
  • Inventa um método authorize? próprio no model

Com "Pundit policies in app/policies/" escrito, o Claude vai direto em app/policies/ adicionar um arquivo de policy — estilo consistente.

Uma linha economiza o "trabalho de detetive" do Claude toda vez.

6. Restrições externas do projeto inteiro

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

Ao adicionar uma feature nova, os defaults do Claude são:

  • Adicionar só strings em inglês
  • Escrever testes com Minitest + fixtures (default do Rails)

Mas seu projeto precisa de fato:

  • Traduções em 3 locales
  • RSpec + FactoryBot, não fixtures

Violar essas "restrições externas do projeto inteiro" gera monte de retrabalho — traduções a completar, testes a reescrever. O CLAUDE.md prega de uma vez "o que tem que ser feito toda vez".

O outro lado: isto é puro desperdício

Tão importante quanto "tem que escrever" é "não escreva". As categorias abaixo, viu = apague:

1. Descrição de feature

"Sistema de júri: usuários podem denunciar conteúdo inadequado, o denunciado entra em votação pública, jurados são sorteados de..."

→ O Claude abre topic_review_service.rb e lê com mais precisão do que você escreveu. Empurrar isso em toda nova conversa é puro desperdício.

2. O que se lê na hora pela árvore de diretórios / Gemfile

"app/models/ tem modelos ActiveRecord", "Usa Rails 8", "Banco é PostgreSQL"

→ O Claude dá uma olhada na raiz do projeto e no Gemfile e sabe.

3. Conhecimento genérico de programação

"Controllers should be thin, delegate to services", "Evite N+1", "Escreva testes das features principais"

→ Já está no treino do Claude. Só escreva se seu projeto for anormal — tipo "de propósito não usamos service layer; a lógica vive nos controllers".

4. Contexto da tarefa atual

"Estamos agora refatorando o sistema de pagamentos; o foco é..."

→ Isso é contexto de conversa, não fato do projeto. Jogar no CLAUDE.md contamina todas as outras conversas.

Auditoria real: meu próprio CLAUDE.md também dá pra cortar pela metade

Coloque a prova de "eu consigo fazer" antes do discurso. Depois de escrever a seção acima, passei meu próprio CLAUDE.md do Pickful — 238 linhas — pelas 5 perguntas. Resultado: cerca de metade é desperdício.

O que cortar (~120 linhas):

Quase todo o bloco de comandos de desenvolvimento (70 linhas → 10): bin/setup / bin/rails db:migrate / bundle exec rspec / bin/rubocop / bin/brakeman são todos comandos Rails padrão, já no treino do Claude. Só ficam os três específicos do projeto: bin/jobs (worker do Solid Queue), bin/importmap pin (específico do ImportMap), bin/kamal deploy.

Lista Core Domain Models (35 linhas → apagar toda): listar 20 modelos com o papel de cada um é o exemplo mais clássico de "CLAUDE.md estilo README" — o Claude roda ls app/models/ ou lê um arquivo de model e sabe. Jogar isso em toda conversa é desperdício puro.

Itens padrão do Tech Stack (28 linhas → 8): Rails 8 / Devise / Pundit / Tailwind / pg_search são todos legíveis na hora pelo Gemfile. Só ficam os contra-intuitivos: Propshaft / ImportMap / Lexxy / x402-rails / Grover.

Conhecimento genérico de programação espalhado: "Controllers should be thin", "Use app/jobs/ for async processing", o que request specs vs. model specs testam — o Claude já faz por default. Só queima token.

O que resta, ~100 linhas: convenções de roteamento URL, divisão dos 4 bancos, thresholds VIP 400 / Hot 15, override do Lexxy, escolhas arquiteturais anti-default, placa Pundit, lista de locales, FactoryBot (não fixtures).

Mais importante: quais invariantes ainda faltam?

Auditando, percebi alguns que eu deveria ter adicionado e nunca adicionei:

  • Números enterrados no sistema de júri (threshold de votação, requisitos de elegibilidade do jurado) — nada exposto no CLAUDE.md
  • Se x402 tem chain id, endereço de contrato, env vars obrigatórias — sem isso o Claude não vai ler o arquivo de config e vai inventar valores
  • Regras específicas dos services de negociação de pontos / Referral

238 linhas comprimidas a 100–120, mais 5–10 linhas de invariantes antes esquecidos — isso está mais próximo da "densidade certa" pra um CLAUDE.md.

Isto não é tutorial. É uma auditoria do meu próprio projeto — e eu também errei. A forma correta de CLAUDE.md é ir apagando e ir adicionando — qualquer projeto que amadureça um pouco deveria ter um CLAUDE.md cada vez mais curto e mais denso.

5 perguntas antes de incluir uma regra

Toda vez que penso em adicionar algo no CLAUDE.md, rodo o checklist abaixo:

  1. O Claude consegue deduzir esta regra lendo 3 arquivos? Se sim — não escreva, deixa ele ler.
  2. Esta regra é contra-intuitiva? (Thresholds incomuns, escolhas de biblioteca fora do mainstream, config que contradiz o default upstream.) Se sim — tem que escrever.
  3. É uma invariante da codebase toda, ou afeta só um arquivo? Se afeta um só — comentário de código, não sobe pro CLAUDE.md.
  4. Violar esta regra faz o Claude errar silenciosamente? (Sem erro, mas semântica errada — banco errado, tradução faltando, policy pulada.) Se sim — obrigatória.
  5. Dá pra explicar em 3 linhas? Se não — você mesmo ainda não digeriu. Não escreva ainda.

Regras que passam nas 5 ficam; qualquer uma que não passa se apaga ou reescreve.

Resumo em uma linha

O uso correto do CLAUDE.md não é "apresentar o projeto", é comprimir o conhecimento tácito entre você e a codebase que o Claude nunca vai preencher lendo código — escolhas incomuns, thresholds invisíveis, convenções que contradizem defaults, restrições externas do projeto inteiro.

Cada linha que você adiciona deve responder: "Isto aqui, o Claude não consegue ler do código?" Não consegue — fica. Consegue — apaga.

Um CLAUDE.md escrito assim costuma ser bem mais que metade do tamanho do primeiro rascunho, mas vale muito mais por conversa do que milhares de palavras de descrição de feature. E ele está sempre "ainda não certo" — toda feature nova revela outro invariante que deveria estar lá, e outro parágrafo que agora dá pra cortar. Apaga e adiciona, apaga e adiciona — essa é a manutenção diária do CLAUDE.md.