好的 CLAUDE.md 不描述功能,只寫 Claude 讀程式碼補不齊的 invariant:6 類必寫、4 類別寫、5 問清單。
我的 Pickful 專案有去中心化社群陪審系統、x402 加密貨幣支付、Sign-In with Ethereum、多資料庫、即時推送——全是這兩年才起來的技術棧。Claude 做這些功能做得又快又好。
但打開專案的 CLAUDE.md,你會發現:陪審系統和 x402,連字眼都沒出現過。
這不是疏忽。CLAUDE.md 的作用從來不是「描述功能」,而是寫那些 Claude 讀一遍程式碼永遠也看不出來的東西。
第一次寫 CLAUDE.md 的人,常常像在寫 README:把每個核心功能都描述一遍。
這些內容 Claude 打開 topic_review_service.rb / x402.rb / like_points_service.rb 就能比你寫得更準。你用一千字描述的業務邏輯,Claude 讀程式碼只花幾百 token,而且不會產生理解偏差——程式碼是事實,描述是二手資訊。
真正會讓 Claude 翻車的,是下面這 6 類內容。
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
每一行都在對抗預設猜測。Claude 看到一個 Rails 專案,預設會假設:
如果不寫進 CLAUDE.md,讓 Claude 加個新 JS 功能,它很可能去裝 Webpacker、改 package.json、寫 bundler 設定——全錯,而且錯得很隱蔽(專案能跑,但資源管線就污染了)。
寫進 CLAUDE.md 的這幾行,就是在告訴 Claude:別猜,已經選好了。
PostgreSQL with 4 separate databases:
- primary - Main application data
- cache - Solid Cache storage
- queue - Solid Queue jobs
- cable - Action Cable subscriptions
這條寫法樸素,但省掉的坑可能是整晚。Rails 8 預設多庫是新行為,Claude 不會主動去查你用幾個庫。一個看似無關的 migration 放錯資料庫,開發時不報錯(四個庫都是 PostgreSQL,schema 寫哪個庫都能 migrate 通過),但生產上 Solid Queue 的 job 表混進 primary 備份、或者 primary 的 model 跑去查 cache 庫——這種錯要跑一陣子才會浮現。
寫進 CLAUDE.md 只要兩行,定位一次線上故障要花一整天。
/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 char、3-4 char)藏在 model 或 service 的 slug 生成邏輯裡。讓 Claude 加個新短網址類型,它大概率會生成一個 6 位、UUID 風格或者純數字的 slug——和你整個系統的視覺語言脫節。
這類「慣例」的特點:違反不會報錯,但會讓後來人看程式碼覺得不對勁。必須寫。
VIP status at 400+ points
Posts with 15+ likes are "hot" posts
這兩個數字都在程式碼裡某處(User#vip?、Post#hot? scope)。問題是 Claude 改一個相關功能時——調整積分獎勵、加一個「即將成為 VIP」的通知、寫一個「熱門文章置頂」的排程任務——它不會主動把其他地方的閾值對齊。
結果:你給完成某個任務獎勵 500 分,但文案寫「可以成為 VIP」(實際 400 就是了);或者給新功能做種子資料時少給了讚,永遠碰不到 15 的門檻。
Claude 的程式能力很強,但它沒有整個系統的數字感。把關鍵閾值放在 CLAUDE.md 裡,讓它每次對話開局就知道「400 和 15 是特殊數字」。
- Devise (authentication) + Pundit (authorization)
- Pundit policies in app/policies/
- Check UserPolicy, PostPolicy, etc. for permission rules
這條的作用是導航,不是描述。
沒有這條,Claude 需要加個新權限控制時有三種可能:
unless current_user.admin? 硬寫authorize? 方法寫上「Pundit policies in app/policies/」這一行,Claude 每次都會直接去 app/policies/ 加 policy 檔案,風格統一。
一行字省掉 Claude 每次的「偵探工作」。
Supported locales: en, zh-CN, zh-TW
Testing stack: RSpec + FactoryBot + Capybara + Shoulda Matchers
加一個新 feature 時,Claude 預設會:
而你的專案實際需要:
這種「專案範圍的外部約束」違反了會產生一堆細碎的後續工作——翻譯要補、測試要重寫。寫進 CLAUDE.md 就是一次性把「每次都必須做的事」釘死。
和「必須寫」同樣重要的是「別寫」。下面這幾類每次看到都刪掉:
1. 功能描述
「陪審系統:使用者可以檢舉違規內容,被檢舉的內容會進入公開投票階段,陪審員從……」
→ Claude 打開 topic_review_service.rb 能讀出來,而且讀得比你寫的更準。每次開新對話都把這段塞進上下文是純浪費。
2. 能從目錄結構 / Gemfile 秒讀出的東西
「app/models/ 放 ActiveRecord 模型」、「使用 Rails 8」、「資料庫用 PostgreSQL」
→ Claude 掃一眼專案根目錄和 Gemfile 就知道了。
3. 通用程式設計常識
「Controllers should be thin, delegate to services」、「避免 N+1 查詢」、「寫測試覆蓋主要功能」
→ 這些在 Claude 的訓練集裡。只有當你的專案反常時才寫——比如「我們故意不寫 service 層,邏輯直接放在 controller」。
4. 當前任務的上下文
「現在我們在做支付系統的重構,重點是……」
→ 這是對話上下文,不是專案事實。塞進 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 訓練集裡的東西。只留專案特有的三條:bin/jobs(Solid Queue worker)、bin/importmap pin(ImportMap 專屬)、bin/kamal deploy。
Core Domain Models 列表(35 行 → 刪光):
把 20 個 model 名字和職責列一遍是最典型的「README 式 CLAUDE.md」——Claude 跑 ls app/models/ 或讀一個 model 檔案就知道,每次對話塞進去全是浪費。
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 預設就會做,寫了只是佔 token。
留下來的大概 100 行:URL 路由慣例、4 資料庫分工、VIP 400 / Hot 15 兩個硬閾值、Lexxy override、反預設的架構選型、Pundit 導航、Locale 列表、FactoryBot(not fixtures)。
但更關鍵的是:還要補哪些沒寫的 invariant?
審計過程中我意識到幾條應該加但一直沒加的:
238 行壓到 100–120 行、再補 5–10 行之前漏掉的關鍵 invariant——這才是更接近「正確密度」的 CLAUDE.md。
這不是教學,是一次對自己專案的審計。我也沒寫對。CLAUDE.md 的正確形態就是不停地刪、不停地補——任何一個專案成熟一點,它應該越來越短、越來越硬。
我每次想往 CLAUDE.md 加內容,會按這個清單過一遍:
5 條都過的規則留下來;有一條答不上來的,刪掉或重寫。
CLAUDE.md 的正確用法不是「介紹專案」,是壓縮你和專案之間那些 Claude 無論怎麼讀程式碼都補不齊的隱性知識——反常的選型、隱形的閾值、違反預設的約定、專案範圍的外部約束。
寫進去的每一行都應該在回答一個問題:「這件事,Claude 讀程式碼讀不出來嗎?」 讀不出來——留下。能讀出來——刪掉。
這樣寫出來的 CLAUDE.md 通常比初版短一半以上,但對 Claude 每一次對話的價值,比幾千字的功能描述大得多。而且它永遠「還沒寫對」——每做一個新功能,都會發現之前漏掉的 invariant 該加、原來寫的某段已經能砍了。不停地刪、不停地補,就是 CLAUDE.md 的日常維護。