Free

良い CLAUDE.md は機能を説明しない——Claude がコードから読み取れないことだけを書く

良い CLAUDE.md は機能説明ではない。Claude がコードから推測できない invariant だけ書く。必須 6 類、不要 4 類、5 つの問い。


私の Pickful プロジェクトには、分散型コミュニティ陪審システム、x402 暗号通貨決済、Sign-In with Ethereum、マルチデータベース、リアルタイムプッシュ——どれもここ 1〜2 年で出てきた技術スタックが入っている。Claude はこれらの機能を速く、きれいに仕上げる。

ところがプロジェクトの CLAUDE.md を開くと、陪審システムx402 という単語は一度も登場しない

これは手抜きではない。CLAUDE.md の役割は「機能を説明する」ことではなく、Claude がコードを一度読んだだけでは決して見抜けないものを書くことだ。

機能説明はトークンの無駄遣い

CLAUDE.md を初めて書く人は、ほぼ README 感覚ですべての主要機能を一通り説明しがちだ。

  • 「陪審システムはユーザーが通報でき、通報されたコンテンツは投票で……」
  • 「x402 決済は HTTP 402 ステータスコードで on-chain 送金を……」
  • 「いいねでポイントが付与され、400 点で VIP に……」

こうした内容は、Claude が topic_review_service.rb / x402.rb / like_points_service.rb を開いた方があなたの文章より正確に把握する。千字で書いたビジネスロジックを Claude はコード経由で数百トークンで読み取り、しかも解釈のブレがない——コードは事実、説明は二次情報だ。

Claude が本当につまずくのは、以下の 6 種類だ。

本当に効く 6 つのカテゴリ

1. 反直感的なアーキ選定

Pickful の CLAUDE.md にはこんな行がある:

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

どの行もデフォルトの推測に逆らっている。Rails プロジェクトを見た Claude はデフォルトで以下を想定する:

  • 静的アセットは Sprockets(古いプロジェクトの慣性)
  • JS は Webpacker か esbuild
  • フロント相互作用は React か Stimulus + Turbo の混在
  • リッチテキストは素の ActionText

これらを CLAUDE.md に書かないまま、Claude に新しい JS 機能を追加させると、高確率で Webpacker を入れ、package.json を書き換え、bundler 設定を書き始める——全部間違いで、しかも静かに間違える(アプリは動くが、アセットパイプラインが汚染される)。

これらの数行は Claude に言っている:推測するな、決まっている

2. マルチデータベースの分担

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

地味な書き方だが、潰せる落とし穴は丸ごと一晩分あり得る。Rails 8 のマルチ DB は新しい挙動で、Claude は自発的に DB 数を確認しない。一見無関係な migration が間違った DB に落ちても開発時にはエラーにならない(4 つとも PostgreSQL、schema はどこでも通る)。だが本番で Solid Queue の job テーブルが primary バックアップに紛れたり、primary のモデルが cache を見に行ったり——この種のバグは時間が経って初めて浮上する。

CLAUDE.md 上の 2 行 vs 本番障害 1 日。

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

Claude は routes.rb からルート自体は読めるが、長さの規約(4-5 chars、3-4 chars)はモデルや service の slug 生成ロジックの中に埋まっている。新しい短縮 URL タイプを追加させると、6 桁、UUID 風、あるいは純数字で slug を生成しがちで——システム全体の視覚的整合性から浮く。

この種の「慣例」は特性が厄介だ:違反してもエラーは出ない。ただ後続の読み手が「なんか違う」と感じる。だから書くしかない。

4. ハードコードされたビジネス閾値

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

数字はどちらもコード中のどこか(User#vip?Post#hot? の scope)にある。問題は、Claude が関連機能を触るとき——ポイント付与の調整、「もうすぐ VIP」通知の追加、Hot Posts を pin するジョブの作成——他所の閾値と整合させようと自発的にはしないこと。

結果、あるタスク完了で 500 点を付与しつつコピーは「VIP になれます」(実は 400 点で足りる)。新機能のシードデータを用意するのにいいねが不足して 15 の閾値に永遠に届かない。

Claude のコーディング力は高いが、システム全体としての数字の感覚はない。重要な閾値を CLAUDE.md に置くと、毎回の会話開始時点で「400 と 15 は特別な数字」と知った状態で始まる。

5. 認証/認可スタックの案内板

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

この行の役割はナビであって説明ではない。

これが無いと、Claude が新しい権限制御を追加するとき 3 通りの可能性が生じる:

  • controller に unless current_user.admin? を直書き
  • もはや使われていない CanCan の残骸を引っ張り出す
  • モデルに独自発明の authorize? メソッドを追加

「Pundit policies in app/policies/」の一行があれば、Claude は毎回 app/policies/ に policy ファイルを追加する——スタイルが揃う。

一行で Claude の毎回の「探偵仕事」を削れる。

6. プロジェクト範囲の外部制約

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

新機能を追加するとき、Claude のデフォルトは:

  • 英語文字列だけ追加
  • Minitest + fixtures でテストを書く(Rails デフォルト)

だが実際に必要なのは:

  • 3 ロケール分の翻訳
  • RSpec + FactoryBot、fixtures じゃない

この種の「プロジェクト範囲の外部制約」を破ると、翻訳補完・テスト書き直しという細かな後始末が大量発生する。CLAUDE.md に書くことで「毎回必ずやること」を一発で固定できる。

反面:これを書くのは無駄

「絶対書く」と同じくらい重要なのが「書かない」。以下を見つけたら即削除:

1. 機能説明

「陪審システム:ユーザーは違反コンテンツを通報でき、通報後は公開投票フェーズに入り、陪審員は……」

→ Claude は topic_review_service.rb を開けばあなたより正確に読み取る。毎回新しい会話にこれを詰め込むのは純粋な無駄。

2. ディレクトリ構造 / Gemfile から即読めること

「app/models/ に ActiveRecord モデル」「Rails 8 を使用」「DB は PostgreSQL」

→ プロジェクトルートと Gemfile をチラ見すれば分かる。

3. 一般的なプログラミング常識

「Controllers should be thin, delegate to services」「N+1 クエリを避ける」「主要機能はテストする」

→ これらは Claude の訓練データに既に入っている。あなたのプロジェクトが異常なときだけ書く——たとえば「我々は意図的に service 層を使わず、ロジックは controller に書く」。

4. 現在タスクの文脈

「今は決済システムをリファクタリング中、焦点は……」

→ これは会話の文脈であってプロジェクトの事実ではない。CLAUDE.md に突っ込むと他の全会話を汚染する。

実戦監査:自分の CLAUDE.md も半分削れる

「自分もできる」を証明してから説く。上のセクションを書き終えた後、自分の 5 つの質問で Pickful の CLAUDE.md——238 行——を一周した。結果:ほぼ半分が無駄だった。

削るべき部分(約 120 行)

開発コマンドのセクションの大部分(70 行 → 10 行):bin/setup / bin/rails db:migrate / bundle exec rspec / bin/rubocop / bin/brakeman は全部標準 Rails コマンド、Claude の訓練データ内。プロジェクト特有の 3 本だけ残す:bin/jobs(Solid Queue worker)、bin/importmap pin(ImportMap 専用)、bin/kamal deploy

Core Domain Models のリスト(35 行 → 全削除):20 個のモデル名とその役割を並べるのは「README スタイル CLAUDE.md」の典型——Claude は ls app/models/ かモデルを 1 ファイル読めば把握する。毎回の会話に積むのは純粋な浪費。

Tech Stack の標準項目(28 行 → 8 行):Rails 8 / Devise / Pundit / Tailwind / pg_search は全部 Gemfile から即読める。反直感的なものだけ残す:Propshaft / ImportMap / Lexxy / x402-rails / Grover。

散らばった一般常識:「Controllers should be thin」「Use app/jobs/ for async processing」、request specs / model specs がそれぞれ何を検証するか——これは Claude がデフォルトでやる。トークンの浪費。

残す約 100 行:URL ルーティング慣例、4 DB 分担、VIP 400 / Hot 15 の 2 つの硬い閾値、Lexxy override、反デフォルトのアーキ選定、Pundit ナビ、Locale リスト、FactoryBot(not fixtures)。

だが、より重要なのは:まだ書かれていない invariant はどれか?

監査中に気付いた、追加すべきで追加していないもの:

  • 陪審システム内に埋もれた数字(投票閾値、陪審員資格要件)——CLAUDE.md に一切露出していない
  • x402 に chain id、コントラクトアドレス、必要な env var がある場合——設定ファイルを Claude は引かないので値を勝手に捏造する
  • ポイント取引 / Referral の service にある特殊な制約ルール

238 行 → 100〜120 行、さらに 5〜10 行の漏れていた invariant を追加——これで「正しい密度」の CLAUDE.md に近づく。

これはチュートリアルではなく、自分のプロジェクトへの監査だ。自分でも書き切れていなかった。CLAUDE.md の正しい形は削り続け、足し続けること——プロジェクトが成熟するほど、CLAUDE.md は短く、密になっていくべきだ。

ルールを追加する前に問う 5 つの質問

CLAUDE.md に何かを加えようとするたび、次のチェックリストを通す:

  1. Claude が 3 ファイル読めば推論できるルールか? できるなら書かない——自分で読ませる。
  2. このルールは直感に反するか?(異常な閾値、主流から外れたライブラリ選定、上流デフォルトに逆らう設定)反直感なら必ず書く。
  3. codebase 全体の invariant か、一つのファイルだけに影響するか? 単一ファイルはコードコメントで、CLAUDE.md に上げない。
  4. このルールを破ると Claude は静かに誤ったことをするか?(エラーは出ないが意味が違う——DB 違い、翻訳漏れ、policy 見落とし)そうなら必ず書く。
  5. 3 行で言い切れるか? 言い切れないなら自分が整理できていない——まだ書くな。

5 項目を通過したルールは残す;一項目でも答えられないルールは削除か書き直し。

一行でまとめると

CLAUDE.md の正しい用途は「プロジェクト紹介」ではなく、あなたとコードベースの間にある、Claude がどう読んでも補完できない暗黙知を圧縮すること——異常な選定、不可視の閾値、デフォルトに反する規約、プロジェクト範囲の外部制約。

書く一行ごとが、この問いに答えているべきだ:「このこと、Claude はコードから読み取れないのか?」 読み取れない——残す。読み取れる——消す。

こうして書かれた CLAUDE.md は初稿より半分以上短くなる。それでいて、毎回の会話に対する価値は、数千字の機能説明より圧倒的に大きい。そしてそれは常に「まだ書き切れていない」——新機能を出すたびに、入れ損ねていた invariant に気付き、以前書いたパラグラフが削れることに気付く。削り続け、足し続ける——それが CLAUDE.md の日常メンテナンス。