Free

CLAUDE.md yang baik tidak mendeskripsikan fitur — hanya menulis apa yang Claude tidak bisa lihat dari membaca kode

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.

Deskripsi fitur = buang-buang token

Orang yang pertama kali menulis CLAUDE.md biasanya memperlakukannya seperti README — mendeskripsikan setiap fitur inti:

  • "Sistem juri memungkinkan pengguna melaporkan konten; yang dilaporkan masuk tahap voting publik, juri dipilih dari..."
  • "Pembayaran x402 memicu transfer on-chain lewat kode status HTTP 402..."
  • "Like memberikan poin; di 400 poin pengguna menjadi VIP..."

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.

6 kategori yang benar-benar menyelamatkan

1. Pilihan arsitektur yang melawan intuisi

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:

  • Aset statis lewat Sprockets (warisan proyek lama)
  • JS pakai Webpacker atau esbuild
  • Frontend pakai React atau campuran Stimulus + Turbo
  • Rich text pakai ActionText bawaan

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.

2. Pembagian multi-database

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.

3. "Konvensi tak terlihat" dari 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

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.

4. Ambang batas bisnis hardcoded

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

5. Penanda navigasi stack auth/authz

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

  • Menulis unless current_user.admin? langsung di controller
  • Menggali sisa CanCan yang sudah tidak dipakai
  • Menambah method authorize? rekaan sendiri di model

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

6. Batasan eksternal seluruh proyek

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

Menambah fitur baru, default Claude:

  • Hanya menambah string bahasa Inggris
  • Menulis test dengan Minitest + fixtures (default Rails)

Padahal yang benar-benar dibutuhkan proyekmu:

  • Tiga locale terjemahan
  • RSpec + FactoryBot, bukan fixtures

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

Sisi sebaliknya: yang ditulis berarti sia-sia

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.

Audit nyata: CLAUDE.md saya sendiri juga bisa dipangkas setengah

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:

  • Angka-angka tersembunyi di sistem juri (ambang voting, syarat kelayakan juri) — sama sekali belum muncul di CLAUDE.md
  • Kalau x402 punya chain id, alamat kontrak, env var wajib — Claude tidak akan mengorek file konfigurasi dan akan mengarang nilainya sendiri
  • Aturan-aturan khusus di service perdagangan poin / Referral

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.

5 pertanyaan sebelum menambah aturan

Setiap kali akan menambah sesuatu ke CLAUDE.md, saya melewatinya lewat checklist berikut:

  1. Apakah Claude bisa menyimpulkan aturan ini dengan membaca 3 file? Kalau bisa — jangan tulis, biarkan dia baca sendiri.
  2. Apakah aturan ini melawan intuisi? (Ambang tak lazim, pilihan library non-mainstream, konfigurasi yang bertentangan dengan default upstream.) Kalau ya — wajib ditulis.
  3. Apakah aturan ini adalah invariant seluruh codebase, atau hanya memengaruhi satu file? Kalau satu file — jadikan komentar kode, bukan naik ke CLAUDE.md.
  4. Apakah melanggar aturan ini akan membuat Claude diam-diam salah? (Tidak error tapi semantiknya salah — salah database, terjemahan terlewat, policy tidak diperiksa.) Kalau ya — wajib ditulis.
  5. Bisakah dijelaskan dalam 3 baris? Kalau tidak bisa — kamu sendiri belum memahami dengan bersih. Jangan tulis dulu.

Aturan yang lolos 5 pertanyaan — tinggalkan. Yang ada satu saja tidak terjawab — hapus atau tulis ulang.

Ringkasan satu kalimat

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.