CLAUDE.md yang baik bukan README — ia menulis invariant yang Claude tak bisa simpulkan dari kode. 6 yang ditulis, 4 yang dihindari, 5 pertanyaan.
Proyek Pickful saya punya sistem juri komunitas terdesentralisasi, pembayaran kripto x402, Sign-In with Ethereum, multi-database, push real-time — semuanya stack teknologi yang baru muncul dalam satu-dua tahun terakhir. Claude mengerjakan fitur-fitur ini cepat dan rapi.
Tapi kalau kamu buka CLAUDE.md proyek ini, kamu akan melihat: sistem juri dan x402 — katanya pun tidak pernah muncul sama sekali.
Ini bukan kelalaian. Tujuan CLAUDE.md memang tidak pernah "mendeskripsikan fitur", melainkan menulis hal-hal yang tidak pernah bisa disimpulkan Claude hanya dengan membaca kode.
Orang yang pertama kali menulis CLAUDE.md biasanya memperlakukannya seperti README — mendeskripsikan setiap fitur inti:
Konten semacam ini, Claude cukup buka topic_review_service.rb / x402.rb / like_points_service.rb dan dia baca lebih akurat dari tulisanmu. Logika bisnis yang kamu jabarkan seribu kata, Claude cukup baca dengan beberapa ratus token, tanpa distorsi interpretasi — kode adalah fakta, deskripsi adalah informasi tangan kedua.
Yang benar-benar bikin Claude terpeleset adalah 6 kategori berikut.
CLAUDE.md Pickful punya beberapa baris seperti ini:
Propshaft (not Sprockets)
ImportMap (no JavaScript bundler)
Hotwire: Turbo Frames, Turbo Streams, Stimulus
Lexxy gem overrides ActionText:
config.lexxy.override_action_text_defaults = false
Setiap baris melawan tebakan default. Melihat proyek Rails, asumsi default Claude adalah:
Tanpa baris ini di CLAUDE.md, minta Claude menambah fitur JS baru, besar kemungkinan dia akan memasang Webpacker, mengubah package.json, menulis konfigurasi bundler — semua salah, dan salahnya senyap (aplikasi masih jalan, tapi pipeline aset tercemar).
Beberapa baris di CLAUDE.md ini bilang ke Claude: jangan menebak, sudah diputuskan.
PostgreSQL with 4 separate databases:
- primary - Main application data
- cache - Solid Cache storage
- queue - Solid Queue jobs
- cable - Action Cable subscriptions
Tulisannya sederhana, tapi jebakan yang dihindari bisa senilai semalam suntuk. Multi-DB default di Rails 8 adalah perilaku baru, Claude tidak akan sengaja mengecek berapa DB yang kamu pakai. Migration yang kelihatan tak terkait mendarat di DB yang salah tidak menghasilkan error saat dev (keempatnya PostgreSQL, schema lolos di mana saja). Tapi di production, tabel job Solid Queue tercampur ke backup primary, atau model primary malah query ke DB cache — bug semacam ini baru muncul setelah beberapa waktu.
Dua baris di CLAUDE.md vs. satu hari menebak bug production.
/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
Route sendiri Claude bisa lihat di routes.rb, tapi konvensi panjang (4-5 karakter, 3-4 karakter) tersembunyi di logika generator slug di model atau service. Minta Claude menambah tipe short link baru, besar kemungkinan dia akan buat slug 6 digit, gaya UUID, atau angka saja — lepas dari bahasa visual seluruh sistem.
Sifat "konvensi" semacam ini: melanggarnya tidak memunculkan error, tapi orang yang baca kode kemudian akan merasa "ada yang aneh". Harus ditulis.
VIP status at 400+ points
Posts with 15+ likes are "hot" posts
Kedua angka ini ada di suatu tempat di kode (User#vip?, scope Post#hot?). Masalahnya, saat Claude mengubah sesuatu yang berdekatan — menyesuaikan reward poin, menambah notifikasi "hampir VIP", menulis cron untuk mem-pin hot post — dia tidak akan otomatis menyelaraskan ambang batas di tempat lain.
Hasilnya: kamu memberi 500 poin sebagai hadiah satu task tapi copy-nya bilang "kamu bisa jadi VIP" (padahal 400 sudah cukup); atau kamu membuat seed data fitur baru dengan jumlah like kurang sehingga tidak pernah mencapai ambang 15.
Kemampuan ngoding Claude kuat, tapi dia tidak punya kepekaan angka lintas-sistem. Menaruh ambang batas kunci di CLAUDE.md berarti setiap awal percakapan dia sudah tahu "400 dan 15 adalah angka khusus".
- Devise (authentication) + Pundit (authorization)
- Pundit policies in app/policies/
- Check UserPolicy, PostPolicy, etc. for permission rules
Peran baris ini adalah navigasi, bukan deskripsi.
Tanpa ini, saat Claude harus menambah kontrol permission baru, ada tiga kemungkinan:
unless current_user.admin? langsung di controllerauthorize? rekaan sendiri di modelDengan "Pundit policies in app/policies/" tertulis, Claude setiap kali akan langsung ke app/policies/ menambah file policy, gaya seragam.
Satu baris menghemat "pekerjaan detektif" Claude setiap kali.
Supported locales: en, zh-CN, zh-TW
Testing stack: RSpec + FactoryBot + Capybara + Shoulda Matchers
Menambah fitur baru, default Claude:
Padahal yang benar-benar dibutuhkan proyekmu:
Melanggar "batasan eksternal seluruh proyek" macam ini memunculkan banyak pekerjaan kecil susulan — terjemahan yang harus ditambal, test yang harus ditulis ulang. Menulisnya di CLAUDE.md berarti menguncinya sekali untuk "hal yang harus dilakukan setiap kali".
Sama pentingnya dengan "wajib ditulis" adalah "jangan ditulis". Kategori berikut, lihat = hapus:
1. Deskripsi fitur
"Sistem juri: pengguna bisa melaporkan konten yang melanggar, konten yang dilaporkan masuk tahap voting publik, juri dipilih dari..."
→ Claude buka topic_review_service.rb, dia baca lebih akurat. Menjejalkan ini ke setiap percakapan baru murni buang-buang.
2. Hal yang langsung terbaca dari struktur folder / Gemfile
"app/models/ berisi model ActiveRecord", "Pakai Rails 8", "Database pakai PostgreSQL"
→ Claude lirik root proyek dan Gemfile sebentar sudah tahu.
3. Pengetahuan pemrograman umum
"Controllers should be thin, delegate to services", "hindari query N+1", "tulis test untuk fitur utama"
→ Ini sudah ada di dataset training Claude. Hanya tulis kalau proyekmu menyimpang — misalnya "kami sengaja tidak pakai service layer, logic langsung di controller".
4. Konteks tugas saat ini
"Saat ini kami sedang refactor sistem pembayaran, fokus pada..."
→ Ini konteks percakapan, bukan fakta proyek. Menaruhnya di CLAUDE.md mencemari semua percakapan lain.
Letakkan bukti "saya bisa melakukannya" di depan imbauan. Setelah menulis bagian di atas, saya menjalankan 5 pertanyaan sendiri terhadap CLAUDE.md Pickful — 238 baris — hasilnya: kira-kira setengahnya sia-sia.
Yang harus dipangkas (sekitar 120 baris):
Sebagian besar bagian development commands (70 baris → 10): bin/setup / bin/rails db:migrate / bundle exec rspec / bin/rubocop / bin/brakeman semuanya perintah Rails standar, sudah ada di training Claude. Hanya tiga yang khas proyek yang disimpan: bin/jobs (Solid Queue worker), bin/importmap pin (khusus ImportMap), bin/kamal deploy.
Daftar Core Domain Models (35 baris → hapus total): mendaftar 20 nama model berikut perannya adalah contoh paling klasik "CLAUDE.md gaya README" — Claude jalankan ls app/models/ atau baca satu file model sudah tahu. Menjejalkannya ke setiap percakapan murni sia-sia.
Item standar di Tech Stack (28 baris → 8): Rails 8 / Devise / Pundit / Tailwind / pg_search semua bisa dibaca seketika dari Gemfile. Sisakan hanya yang melawan intuisi: Propshaft / ImportMap / Lexxy / x402-rails / Grover.
Beberapa pengetahuan pemrograman umum yang berceceran: "Controllers should be thin", "Use app/jobs/ for async processing", request specs / model specs menguji apa — ini yang Claude kerjakan default. Hanya boros token.
Yang tersisa sekitar 100 baris: konvensi routing URL, pembagian 4 DB, ambang VIP 400 / Hot 15, override Lexxy, pilihan arsitektur melawan default, navigasi Pundit, daftar locale, FactoryBot (bukan fixtures).
Tapi yang lebih penting: invariant mana yang belum ditulis?
Saat audit saya sadar ada beberapa yang seharusnya ditambah tapi tidak pernah ditambahkan:
238 baris dipangkas jadi 100–120 baris, lalu ditambah 5–10 baris invariant kunci yang sebelumnya luput — ini lebih mendekati "kepadatan benar" sebuah CLAUDE.md.
Ini bukan tutorial, ini audit terhadap proyek saya sendiri. Saya pun belum menulis dengan benar. Bentuk CLAUDE.md yang benar adalah terus menghapus dan terus menambah — setiap proyek yang makin matang seharusnya CLAUDE.md-nya makin pendek dan makin padat.
Setiap kali akan menambah sesuatu ke CLAUDE.md, saya melewatinya lewat checklist berikut:
Aturan yang lolos 5 pertanyaan — tinggalkan. Yang ada satu saja tidak terjawab — hapus atau tulis ulang.
Penggunaan CLAUDE.md yang benar bukan "memperkenalkan proyek", melainkan mengompres pengetahuan tersembunyi antara kamu dan codebase yang tidak pernah bisa dilengkapi Claude dengan membaca kode — pilihan tak lazim, ambang tak terlihat, konvensi melawan default, batasan eksternal seluruh proyek.
Setiap baris yang ditulis harus menjawab satu pertanyaan: "Ini, apakah Claude tidak bisa membaca keluar dari kode?" Tidak bisa — tinggalkan. Bisa — hapus.
CLAUDE.md yang ditulis dengan cara ini biasanya lebih dari setengah lebih pendek dari draf pertamanya, tapi nilainya untuk setiap percakapan jauh lebih besar daripada ribuan kata deskripsi fitur. Dan dia selalu "belum sempurna ditulis" — setiap fitur baru yang diluncurkan menampilkan invariant baru yang luput, dan paragraf lama yang kini bisa dipangkas. Hapus dan tambah, hapus dan tambah — itu adalah pemeliharaan harian CLAUDE.md.