免費

好的 CLAUDE.md 不描述功能——只寫 Claude 看程式碼看不出來的東西

好的 CLAUDE.md 不描述功能,只寫 Claude 讀程式碼補不齊的 invariant:6 類必寫、4 類別寫、5 問清單。


我的 Pickful 專案有去中心化社群陪審系統、x402 加密貨幣支付、Sign-In with Ethereum、多資料庫、即時推送——全是這兩年才起來的技術棧。Claude 做這些功能做得又快又好。

但打開專案的 CLAUDE.md,你會發現:陪審系統x402,連字眼都沒出現過。

這不是疏忽。CLAUDE.md 的作用從來不是「描述功能」,而是寫那些 Claude 讀一遍程式碼永遠也看不出來的東西

功能描述寫進去就是浪費 token

第一次寫 CLAUDE.md 的人,常常像在寫 README:把每個核心功能都描述一遍。

  • 「陪審系統允許使用者檢舉內容,被檢舉後其他使用者投票決定……」
  • 「x402 支付透過 HTTP 402 狀態碼觸發鏈上轉帳……」
  • 「按讚會給使用者加分,累積 400 分變 VIP……」

這些內容 Claude 打開 topic_review_service.rb / x402.rb / like_points_service.rb 就能比你寫得更準。你用一千字描述的業務邏輯,Claude 讀程式碼只花幾百 token,而且不會產生理解偏差——程式碼是事實,描述是二手資訊

真正會讓 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

每一行都在對抗預設猜測。Claude 看到一個 Rails 專案,預設會假設:

  • 靜態資源走 Sprockets(老專案的慣性)
  • JS 用 Webpacker 或 esbuild
  • 前端互動要麼 React、要麼 Stimulus + Turbo 混搭
  • 富文本就是原生 ActionText

如果不寫進 CLAUDE.md,讓 Claude 加個新 JS 功能,它很可能去裝 Webpacker、改 package.json、寫 bundler 設定——全錯,而且錯得很隱蔽(專案能跑,但資源管線就污染了)。

寫進 CLAUDE.md 的這幾行,就是在告訴 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 預設多庫是新行為,Claude 不會主動去查你用幾個庫。一個看似無關的 migration 放錯資料庫,開發時不報錯(四個庫都是 PostgreSQL,schema 寫哪個庫都能 migrate 通過),但生產上 Solid Queue 的 job 表混進 primary 備份、或者 primary 的 model 跑去查 cache 庫——這種錯要跑一陣子才會浮現。

寫進 CLAUDE.md 只要兩行,定位一次線上故障要花一整天。

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 char、3-4 char)藏在 model 或 service 的 slug 生成邏輯裡。讓 Claude 加個新短網址類型,它大概率會生成一個 6 位、UUID 風格或者純數字的 slug——和你整個系統的視覺語言脫節。

這類「慣例」的特點:違反不會報錯,但會讓後來人看程式碼覺得不對勁。必須寫。

4. 硬編碼的業務閾值

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 是特殊數字」。

5. 認證/授權棧的定位

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

這條的作用是導航,不是描述。

沒有這條,Claude 需要加個新權限控制時有三種可能:

  • 在 controller 裡用 unless current_user.admin? 硬寫
  • 找一個已經不在用的 CanCan 殘留
  • 在 model 裡加一個自己發明的 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

加一個新 feature 時,Claude 預設會:

  • 只加英文字串
  • 用 Minitest + fixtures 寫測試(Rails 預設)

而你的專案實際需要:

  • 3 份 locale 翻譯
  • RSpec + FactoryBot,不是 fixtures

這種「專案範圍的外部約束」違反了會產生一堆細碎的後續工作——翻譯要補、測試要重寫。寫進 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 會污染所有其他對話。

實戰審計:我自己的 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?

審計過程中我意識到幾條應該加但一直沒加的:

  • 陪審系統裡藏著的數字(投票門檻、陪審員資格要求)——完全沒暴露到 CLAUDE.md
  • x402 如果有 chain id、合約地址、必需的 env var——Claude 不去查設定檔會自己瞎編
  • 積分交易 / Referral 這些 service 裡的特殊約束規則

238 行壓到 100–120 行、再補 5–10 行之前漏掉的關鍵 invariant——這才是更接近「正確密度」的 CLAUDE.md。

這不是教學,是一次對自己專案的審計。我也沒寫對。CLAUDE.md 的正確形態就是不停地刪、不停地補——任何一個專案成熟一點,它應該越來越短、越來越硬。

寫一條規則前先問自己 5 個問題

我每次想往 CLAUDE.md 加內容,會按這個清單過一遍:

  1. Claude 讀 3 個檔案能推出這條規則嗎? 能就別寫,讓它自己讀。
  2. 這條規則違反直覺嗎?(反常的閾值、反主流的套件選型、和上游預設行為相反的設定)反常就必須寫。
  3. 這條規則是 codebase 範圍的 invariant,還是只影響一個檔案? 單檔案的寫程式碼註解,不上升到 CLAUDE.md。
  4. 違反這條規則會讓 Claude 悄悄地做錯事嗎?(不報錯但語義錯——放錯資料庫、少寫翻譯、漏查 policy)會就必須寫。
  5. 能用 3 行講清嗎? 講不清說明你自己還沒想清楚,先別寫。

5 條都過的規則留下來;有一條答不上來的,刪掉或重寫。

一條話總結

CLAUDE.md 的正確用法不是「介紹專案」,是壓縮你和專案之間那些 Claude 無論怎麼讀程式碼都補不齊的隱性知識——反常的選型、隱形的閾值、違反預設的約定、專案範圍的外部約束。

寫進去的每一行都應該在回答一個問題:「這件事,Claude 讀程式碼讀不出來嗎?」 讀不出來——留下。能讀出來——刪掉。

這樣寫出來的 CLAUDE.md 通常比初版短一半以上,但對 Claude 每一次對話的價值,比幾千字的功能描述大得多。而且它永遠「還沒寫對」——每做一個新功能,都會發現之前漏掉的 invariant 該加、原來寫的某段已經能砍了。不停地刪、不停地補,就是 CLAUDE.md 的日常維護。