Free

Dobry CLAUDE.md nie opisuje funkcji — zapisuje tylko to, czego Claude nie zobaczy czytając kod

Dobry CLAUDE.md to nie README — zapisuje inwarianty, których Claude nie wywnioskuje z kodu. 6 do napisania, 4 do pominięcia, 5 pytań.


Mój projekt Pickful ma zdecentralizowany system ławy przysięgłych społeczności, płatności kryptowalutowe x402, Sign-In with Ethereum, multi-database, push w czasie rzeczywistym — wszystkie stacki, które pojawiły się w ciągu ostatnich dwóch lat. Claude dostarcza te funkcje szybko i czysto.

Ale otwórz CLAUDE.md projektu, a zauważysz: system ławy przysięgłych i x402 — ani razu nie padają.

To nie przeoczenie. Cel CLAUDE.md nigdy nie polegał na „opisywaniu funkcji". Polega na uchwyceniu tego, czego Claude nigdy nie wywnioskuje, czytając kod.

Opisywanie funkcji = marnowanie tokenów

Kto pisze CLAUDE.md po raz pierwszy, często traktuje go jak README — opisując każdą kluczową funkcję:

  • „System ławy przysięgłych pozwala użytkownikom zgłaszać treści; zgłoszone trafia do publicznego głosowania, przysięgli są wybierani z..."
  • „Płatności x402 wywołują przelewy on-chain przez kod statusu HTTP 402..."
  • „Polubienia dają punkty; po 400 punktach użytkownik staje się VIP..."

Taką treść Claude, otwierając topic_review_service.rb / x402.rb / like_points_service.rb, odczyta dokładniej niż to napiszesz. Tysiącsłowny opis logiki biznesowej kosztuje Claude'a kilkaset tokenów odczytania z kodu — bez przesunięcia interpretacyjnego. Kod to fakt. Opisy to informacja z drugiej ręki.

To, na czym Claude naprawdę się potyka, to następujące 6 kategorii.

6 kategorii, które naprawdę ratują

1. Kontrintuicyjne wybory architektoniczne

CLAUDE.md Pickfula ma takie linie:

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

Każda linia stawia opór domyślnemu zgadywaniu. Widząc projekt Rails, Claude domyślnie zakłada:

  • Statyczne zasoby idą przez Sprockets (inercja starych projektów)
  • JS używa Webpackera albo esbuilda
  • Front to React albo mieszanka Stimulus + Turbo
  • Rich text to czysty ActionText

Bez tych linii w CLAUDE.md, poproś Claude'a o dodanie nowej funkcji JS: z dużym prawdopodobieństwem zainstaluje Webpackera, zmodyfikuje package.json, napisze config bundlera — wszystko źle, i błąd cichy (aplikacja działa, ale pipeline zasobów jest zanieczyszczony).

Te linie w CLAUDE.md mówią Claude'owi: nie zgaduj, już zdecydowano.

2. Podział multi-database

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

Prosto napisane, ale może oszczędzić całą noc. Domyślna multi-DB w Rails 8 to nowe zachowanie — Claude sam nie sprawdzi, ilu baz używasz. Niewinnie wyglądająca migracja ląduje w niewłaściwej bazie, w dev nie ma błędu (wszystkie cztery to PostgreSQL, schema migruje wszędzie). Ale na produkcji tabela job z Solid Queue wciska się do backupu primary, albo model z primary robi zapytanie do bazy cache — takie bugi wypływają po dniach.

Dwie linie w CLAUDE.md vs. cały dzień debugowania produkcji.

3. „Niewidzialne konwencje" routingu 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

Trasy Claude widzi w routes.rb, ale konwencje długości (4-5 znaków, 3-4 znaki) są zakopane w logice generowania slugów w modelach lub serwisach. Poproś Claude'a o nowy typ short linka — prawdopodobnie wygeneruje 6-znakowy slug, w stylu UUID albo czysto cyfrowy — niezgodny z językiem wizualnym całego systemu.

Cecha tych „konwencji": łamanie ich nie daje błędu, ale następny czytelnik kodu poczuje, że coś nie gra. Trzeba pisać.

4. Zahardkodowane progi biznesowe

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

Obie liczby żyją gdzieś w kodzie (User#vip?, scope Post#hot?). Problem: gdy Claude zmienia coś powiązanego — dostosowanie nagród punktowych, dodanie powiadomienia „prawie VIP", napisanie crona przypinającego hot posty — nie wyrówna automatycznie progów w innych miejscach.

Efekt: nagradzasz zadanie 500 punktami, ale copy mówi „możesz zostać VIP" (400 wystarczy); albo seedujesz dane dla nowej funkcji z małą liczbą polubień, i próg 15 nigdy nie zostaje przekroczony.

Zdolności Claude'a w kodzie są mocne, ale nie ma ogólnosystemowego wyczucia liczb. Umieszczenie kluczowych progów w CLAUDE.md sprawia, że każda rozmowa zaczyna się z wiedzą „400 i 15 to liczby specjalne".

5. Drogowskaz do stacka auth/authz

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

Rolą tej linii jest nawigacja, nie opis.

Bez niej, gdy Claude musi dodać nową kontrolę uprawnień, trzy możliwości:

  • Pisze unless current_user.admin? wprost w kontrolerze
  • Wykopuje pozostałości CanCan, już nieużywane
  • Wymyśla własną metodę authorize? w modelu

Z „Pundit policies in app/policies/" zapisanym, Claude za każdym razem idzie wprost do app/policies/ dodać plik policy — styl spójny.

Jedna linia eliminuje „pracę detektywistyczną" Claude'a za każdym razem.

6. Ograniczenia zewnętrzne na poziomie projektu

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

Dodając nową funkcję, domyślne zachowania Claude'a to:

  • Dodać tylko angielskie stringi
  • Napisać testy z Minitest + fixtures (domyślny Rails)

Ale twój projekt faktycznie potrzebuje:

  • Tłumaczeń w 3 locale
  • RSpec + FactoryBot, nie fixtures

Łamanie tych „ograniczeń zewnętrznych na poziomie projektu" generuje stos drobnej pracy na potem — tłumaczenia do uzupełnienia, testy do przepisania. Zapis w CLAUDE.md wbija raz na zawsze „rzeczy, które trzeba robić za każdym razem".

Druga strona: tego pisanie to marnotrawstwo

Tak samo ważne jak „trzeba pisać" jest „nie pisz". Poniższe kategorie — widzisz = usuwasz:

1. Opisy funkcji

„System ławy przysięgłych: użytkownicy mogą zgłaszać treści łamiące zasady, zgłoszone wchodzi w etap publicznego głosowania, przysięgli wybierani są z..."

→ Claude otworzy topic_review_service.rb i przeczyta dokładniej niż ty napisałeś. Wpychanie tego do każdej nowej rozmowy to czyste marnotrawstwo.

2. To, co natychmiast odczytasz z drzewa katalogów / Gemfile

„app/models/ zawiera modele ActiveRecord", „Używa Rails 8", „BD to PostgreSQL"

→ Claude rzuci okiem na korzeń projektu i Gemfile — wie.

3. Ogólna wiedza programistyczna

„Controllers should be thin, delegate to services", „Unikaj N+1", „Pisz testy dla głównych funkcji"

→ To już jest w danych treningowych Claude'a. Pisz to tylko jeśli twój projekt jest nietypowy — np. „celowo nie używamy warstwy service; logika żyje w kontrolerach".

4. Kontekst bieżącego zadania

„Obecnie refaktorujemy system płatności; fokus na..."

→ To kontekst rozmowy, nie fakt projektu. Wrzucone w CLAUDE.md, zanieczyści wszystkie inne rozmowy.

Audyt polowy: mój własny CLAUDE.md też da się skrócić o połowę

Połóż dowód „umiem to zrobić" przed kazaniem. Po napisaniu powyższej sekcji przepuściłem własny CLAUDE.md Pickfula — 238 linii — przez 5 pytań. Wynik: około połowa to marnotrawstwo.

Do wycięcia (~120 linii):

Większość bloku dev commands (70 linii → 10): bin/setup / bin/rails db:migrate / bundle exec rspec / bin/rubocop / bin/brakeman to wszystko standardowe komendy Rails, już w treningu Claude'a. Zostawić tylko trzy specyficzne dla projektu: bin/jobs (worker Solid Queue), bin/importmap pin (specyficzne dla ImportMap), bin/kamal deploy.

Lista Core Domain Models (35 linii → skasować całą): wymienianie 20 modeli z rolami to klasyczny przykład „CLAUDE.md w stylu README" — Claude uruchomi ls app/models/ albo przeczyta jeden plik modelu i wie. Pakowanie do każdej rozmowy to czyste marnotrawstwo.

Standardowe pozycje w Tech Stack (28 linii → 8): Rails 8 / Devise / Pundit / Tailwind / pg_search — wszystko natychmiast czytelne z Gemfile. Zostawić tylko kontrintuicyjne: Propshaft / ImportMap / Lexxy / x402-rails / Grover.

Rozproszone ogólniki programistyczne: „Controllers should be thin", „Use app/jobs/ for async processing", co testują request specs vs. model specs — Claude robi to domyślnie. Zbędne palenie tokenów.

Zostaje ~100 linii: konwencje routingu URL, podział 4 DB, progi VIP 400 / Hot 15, override Lexxy, anty-default wybory architektoniczne, drogowskaz Pundit, lista locale, FactoryBot (nie fixtures).

Ale co ważniejsze: których inwariantów jeszcze nie ma?

Podczas audytu zdałem sobie sprawę z kilku rzeczy, które powinienem dodać, a nigdy nie dodałem:

  • Liczby zakopane w systemie ławy przysięgłych (próg głosowania, wymogi kwalifikacji przysięgłego) — w ogóle nie ujawnione w CLAUDE.md
  • Jeśli x402 ma chain id, adres kontraktu, obowiązkowe env vars — bez tego Claude nie zajrzy do pliku configu i wymyśli wartości
  • Specjalne reguły ograniczeń w serwisach point-trading / Referral

238 linii zgnieść do 100–120, plus 5–10 linii wcześniej pominiętych inwariantów — to bliżej „właściwej gęstości" CLAUDE.md.

To nie tutorial. To audyt mojego własnego projektu — i też napisałem źle. Właściwa forma CLAUDE.md to ciągłe kasowanie i ciągłe dodawanie — każdy projekt, który trochę dojrzewa, powinien mieć coraz krótszy i gęstszy CLAUDE.md.

5 pytań przed dodaniem reguły

Za każdym razem, gdy chcę coś dodać do CLAUDE.md, przepuszczam to przez następujący checklist:

  1. Czy Claude wywnioskuje tę regułę czytając 3 pliki? Tak — nie pisz, niech sam czyta.
  2. Czy reguła jest kontrintuicyjna? (Nietypowe progi, niemainstreamowy wybór biblioteki, config sprzeczny z upstream-defaults.) Tak — obowiązkowo pisać.
  3. Czy to inwariant poziomu całej codebase, czy dotyczy tylko jednego pliku? Jeden plik — komentarz w kodzie, nie w CLAUDE.md.
  4. Czy złamanie reguły sprawi, że Claude po cichu zrobi źle? (Bez błędu, ale semantyka zła — zła DB, brakujące tłumaczenie, pominięta policy.) Tak — obowiązkowa.
  5. Czy da się wyjaśnić w 3 liniach? Nie — sam jeszcze nie przemyślałeś. Na razie nie pisz.

Reguły, które przechodzą wszystkie 5, zostają; te, które nie przechodzą jednej, kasujemy albo przepisujemy.

Podsumowanie w jednej linii

Poprawne użycie CLAUDE.md to nie „przedstawianie projektu", ale kompresja wiedzy ukrytej między tobą a codebase'em, której Claude nigdy nie uzupełni czytaniem kodu — nietypowe wybory, niewidzialne progi, konwencje sprzeczne z defaults, ograniczenia zewnętrzne na poziomie projektu.

Każda dodana linia musi odpowiadać na pytanie: „Czy to coś, czego Claude nie odczyta z kodu?" Nie odczyta — zostawić. Odczyta — usunąć.

CLAUDE.md napisany w ten sposób jest zwykle ponad o połowę krótszy od pierwszej wersji, ale na każdą rozmowę wart znacznie więcej niż tysiące słów opisu funkcji. I jest zawsze „jeszcze nieskończony" — każda wydana nowa funkcja ujawnia kolejny inwariant, który powinien był być w środku, i kolejny akapit, który teraz można wyciąć. Usuwać i dodawać, usuwać i dodawać — to codzienna konserwacja CLAUDE.md.