大家好,我是二哥呀。
前兩天有個讀者私訊我,說騰訊二面問了這樣一個問題:「CLAUDE.md 你是怎麼維護的?」他知道的就是「/init 一下」,其他平常也沒怎麼關注。
這就好比面試官想讓你回答「資料庫怎麼優化的」,結果你回答了「裝個 MySQL 就行了」。
說了,但又好像沒說。

/init 只是起點。
CLAUDE.md 這個檔案,寫好了就是超級外掛,你的程式碼交付品質杠杠的。
寫爛了就是 token 噪音。今天這篇內容,給大家來一次硬核拆解。
系好安全帶,我們粗粗粗發~
先把概念捋清楚。
CLAUDE.md 不是 README,不是註解,不是文件,它是 Claude Code 每次啟動時自動讀取的持久化指令檔案。
每次開一個新會話,Claude Code 做的第一件事就是把 CLAUDE.md 的內容塞進上下文視窗。寫在裡面的每一條指令,Claude 在整個會話過程中都能「看到」。
打個比方:CLAUDE.md 就是給 Claude 寫的一份「入職須知」。新員工第一天上班,不可能把所有業務邏輯口頭講一遍,對吧?
得寫一份文件,告訴他:我們用什麼技術棧、程式碼風格怎麼要求、測試怎麼跑、哪些坑千萬別踩。
CLAUDE.md 幹的就是這個事。

TIPS:為了相容 Codex,我通常會讓 CLAUDE.md 去載入一下 AGENTS.md,省得一個專案維護兩份規則。
那 /init 做了什麼呢?
它會掃描程式碼倉庫,分析 package.json、Makefile、README 這些檔案,然後自動生成一份 CLAUDE.md。這份自動生成的檔案通常包含:建置命令、測試命令、專案結構描述。
Claude Code 有一套完整的載入體系,可以分為四層,搞懂這個體系,才能真正用好 CLAUDE.md。

路徑是 ~/.claude/CLAUDE.md,所有專案都會載入。適合放個人的編碼偏好,比如「我喜歡用 2 空格縮排」「commit message 用英文」「別給我寫註解,程式碼要自我說明」。
markdown 體驗 AI 程式碼助手 程式碼解讀複製代碼# ~/.claude/CLAUDE.md
- 使用 2 空格縮排
- commit message 用英文,遵循 Conventional Commits
- 不寫註解,用有意義的變數名和函式名代替
路徑是專案根目錄下的 CLAUDE.md 或 .claude/CLAUDE.md,提交到 git 裡,團隊共享。這是用得最多的一層,寫建置命令、程式碼規範、架構約定。
路徑是 CLAUDE.local.md 或 .claude/CLAUDE.local.md,加到 .gitignore 裡,只在本地生效。適合放個人的環境變數、除錯偏好、或者正在試驗的新規則。
比如正在搞一個新功能,想讓 Claude 這段時間多寫日誌方便除錯,就可以暫時加到 local 檔案裡,不影響團隊其他人。
子目錄下也可以有自己的 CLAUDE.md,但這一層是按需載入的,只有當 Claude 讀取到那個目錄下的檔案時,對應的 CLAUDE.md 才會被載入進上下文。
這個設計對 monorepo 特別友好。前端目錄可以有自己的規則,後端目錄有另一套,互不干擾。
perl 體驗 AI 程式碼助手 程式碼解讀複製代碼my-monorepo/
├── CLAUDE.md # 全域專案規則
├── frontend/
│ └── CLAUDE.md # React 相關規則,按需載入
├── backend/
│ └── CLAUDE.md # Java/Spring 相關規則,按需載入
└── infra/
└── CLAUDE.md # 部署相關規則,按需載入
載入順序是從檔案系統根目錄一路往下走到啟動 Claude Code 的工作目錄。所有檔案的內容會拼接在一起。如果兩個檔案的指令衝突了,靠近工作目錄的那個會因為「最後被讀到」而有更高的優先級。
我把 Claude Code 的原始碼翻出來給大家看看。
核心邏輯在 src/utils/claudemd.ts 的 getMemoryFiles() 函式裡:
typescript 體驗 AI 程式碼助手 程式碼解讀複製代碼// 第一步:載入 Managed(系統級)和 User(使用者級)的 CLAUDE.md + rules
const managedClaudeMd = getMemoryPath('Managed')
result.push(...(await processMemoryFile(managedClaudeMd, 'Managed', ...)))
const userClaudeMd = getMemoryPath('User')
result.push(...(await processMemoryFile(userClaudeMd, 'User', ...)))
// 第二步:從目前目錄一路往上走到根目錄,收集所有路徑
const dirs: string[] = []
let currentDir = getOriginalCwd()
while (currentDir !== parse(currentDir).root) {
dirs.push(currentDir)
currentDir = dirname(currentDir)
}
// 第三步:反轉!從根目錄往下走,依序載入每個目錄的 CLAUDE.md
for (const dir of dirs.reverse()) {
// 載入 CLAUDE.md(Project 類型)
const projectPath = join(dir, 'CLAUDE.md')
result.push(...(await processMemoryFile(projectPath, 'Project', ...)))
// 載入 .claude/CLAUDE.md(也是 Project 類型)
const dotClaudePath = join(dir, '.claude', 'CLAUDE.md')
result.push(...(await processMemoryFile(dotClaudePath, 'Project', ...)))
// 載入 .claude/rules/*.md
const rulesDir = join(dir, '.claude', 'rules')
result.push(...(await processMdRules({ rulesDir, type: 'Project', ... })))
// 載入 CLAUDE.local.md(Local 類型)
const localPath = join(dir, 'CLAUDE.local.md')
result.push(...(await processMemoryFile(localPath, 'Local', ...)))
}
先收集路徑,然後 dirs.reverse() 反轉,從根目錄開始往工作目錄走。越靠近工作目錄的檔案越晚載入,所以優先級越高。而且 Project 和 Local 是在同一個迴圈裡處理的,CLAUDE.local.md 在 CLAUDE.md 之後載入,天然就能覆蓋專案規則。
注意,不要讓不同層級的 CLAUDE.md 互相矛盾。這是維護的第一原則。
CLAUDE.md 寫多了會怎麼樣?Anthropic 官方文件原話是這麼說的:
If your CLAUDE.md is too long, Claude ignores half of it because important rules get lost in the noise.
翻譯一下就是:CLAUDE.md 太長,Claude 會忽略一半,因為重要規則會被雜訊淹沒。

到底多長算「太長」?
這裡要先搞清楚一個概念:什麼是「指令」?
CLAUDE.md 裡寫的每一條規則,就是一條指令。比如「日誌用 SLF4J,不用 System.out」是一條,「改了命令入口要同步 Main.java + CliCommandParser + 測試 + 文件」也是一條。每條指令通常就是一兩句話,大約 10-30 個 token。
arXiv 上有一篇論文《How Many Instructions Can LLMs Follow at Once?》(作者 Daniel Jaroslawicz 等人,論文編號 2507.11538),專門測了模型同時遵循多條指令的能力。

測試方法:給模型一個寫報告的任務,同時附加 N 條約束,比如「必須包含關鍵字 X」「不得用被動語態」「段落不超過 5 句」,然後看模型到底能遵守多少條。
結論是:即使是最強的前沿模型,在 500 條指令密度下準確率也只有 68%。
指令越多,遵循率越低,而且模型會系統性地偏向序列前面的指令,後面的指令更容易被忽略。
注意,這裡的瓶頸不是上下文視窗裝不下——而是模型的注意力分配不過來。就像一個人同時記 500 條規矩,不是紙寫不下,是腦子跟不上。

再算一下 Claude Code 的情況。系統提示本身就帶了大量內建指令(權限控制、工具使用規範、安全約束、程式碼風格要求等),這些已經占了相當多的指令位。
CLAUDE.md 的內容是疊加在這些之上的。所以留給 CLAUDE.md 的有效空間,真的沒有大家想像得那麼多。
這就像給新員工寫入職須知。
寫 10 條,他能記住 8 條。寫 50 條,他能記住 20 條。寫 200 條,翻都不想翻,記住的可能還不到 10 條。
問兩個問題:
第一,如果不寫這條,Claude 會不會搞錯?如果 Claude 靠讀程式碼就能推斷出來,別寫。
第二,這條指令是不是每次會話都需要?如果只在特定場景下需要,放到 rules/ 目錄裡做路徑限定(後面講),別塞在 CLAUDE.md 裡浪費預算。

Anthropic 官方給了一個非常實用的建議:像維護程式碼一樣維護 CLAUDE.md。
定期 review,發現 Claude 沒遵守的指令,加「重要」或「務必要執行」強調;發現 Claude 本來就會做對的指令,果斷刪掉。

指令預算告訴我們 CLAUDE.md 要寫得精簡一些,那就要分清楚哪些規則有用、哪些是噪音。

拿 PaiCLI 專案來舉例。PaiCLI 是一個純 Java 實現的 Agent CLI。我直接把它的 AGENTS.md 裡的規則拿出來,大家感受一下真實的高效規則是什麼樣的:
markdown 體驗 AI 程式碼助手 程式碼解讀複製代碼- 建置:mvn clean package(預設跳過測試)
- 快速回歸:mvn test -Pquick
- 指定測試:mvn test -Dtest=ToolRegistryTest
- search_code 是 RAG 語意輔助,不是主要的程式碼定位方式,優先用 glob_files → grep_code → read_file
- 改了行為 → 同步 AGENTS.md + README.md + ROADMAP.md
- 改了命令入口 → Main.java + CliCommandParser.java + 測試 + 文件
- 禁止提交 .env、真實 API Key、target/ 產物
每一條不說清楚就一定會搞錯。
Claude 不可能從程式碼裡猜到 mvn clean package 預設跳過測試。也不可能知道改了一個斜線命令之後要同步四個地方。更不可能知道 search_code 在這個專案裡只是輔助,真正的程式碼定位靠 glob + grep + read 三件套。
這就是高效規則的樣子:一句話說清楚,不解釋不廢話,但資訊密度很高。
再看看反面例子:
markdown 體驗 AI 程式碼助手 程式碼解讀複製代碼- 使用 Java 17 編寫程式碼
- 遵循分層架構
- 保持程式碼整潔
Claude 看了 pom.xml 裡的 <java.version>17</java.version> 自然就知道用 Java 17,看了 agent/tool/cli/memory 這些目錄自然就知道怎麼分層。「保持程式碼整潔」更是等於沒說。

總結一下,好規則有三個特徵:
第一,一句話能寫完。如果一條規則需要三行才能說清楚,要麼拆成三條,要麼說明它本身就太複雜了,應該放到程式碼註解或文件裡。
第二,Claude 靠自己推斷不出來。能從 pom.xml、程式碼結構、設定檔推斷的都不用寫。
第三,有明確的行動指引。「注意安全」是空話,「PathGuard 限制在專案根目錄,禁止絕對路徑逃逸和符號連結穿越」才是規則。
來看看 Anthropic 的專案是怎麼寫的。
他們的 claude-code-action 倉庫(就是 GitHub Actions 裡跑 Claude Code 的那個專案)有一份 CLAUDE.md,我去翻了一下:

CLAUDE.md 只有一個檔案,專案複雜了之後很容易臃腫。這時候 .claude/rules/ 目錄就派上用場了。
rules 目錄下的每個 .md 檔案都是一份獨立的指令集。沒有 paths 前置欄位的 rules 檔案,和 CLAUDE.md 一樣在啟動時載入。帶 paths 前置欄位的,只在 Claude 操作匹配路徑的檔案時才載入。
markdown 體驗 AI 程式碼助手 程式碼解讀複製代碼# .claude/rules/react-conventions.md
---
paths:
- "src/components/\*_/_.tsx"
- "src/hooks/\*_/_.ts"
---
- 元件用函式式寫法,不用 class
- props 在函式簽名中解構
- 自訂 hook 以 use 開頭
- 狀態管理用 zustand,不用 redux
這份規則只在 Claude 讀寫 src/components/ 或 src/hooks/ 下的檔案時才會被載入。寫後端 Java 程式碼的時候,這些前端規則不會占用上下文視窗,不浪費指令預算。
bash 体验 AI 程式碼助手 程式碼解讀複製代碼.claude/
├── CLAUDE.md # 核心規則,控制在 80 行以內
└── rules/
├── code-style.md # 通用程式碼風格,無路徑限定
├── testing.md # 測試約定,無路徑限定
├── security.md # 安全規則,無路徑限定
├── frontend.md # 前端規則,paths: ["src/**/*.tsx"]
└── api.md # API 規則,paths: ["src/api/**/*.ts"]
CLAUDE.md 只放最核心的 5-10 條規則和關鍵命令,其餘按主題拆到 rules 目錄。這樣每條規則都能被精準投放,不浪費一個 token 的上下文空間。
還有一個進階用法:用 @path 語法匯入外部檔案。
markdown 體驗 AI 程式碼助手 程式碼解讀複製代碼# CLAUDE.md
@README.md
@docs/architecture.md
## 專案規則
- 所有 API 回傳統一用 Result 包裝
- 日誌用 SLF4J,不用 System.out
@README.md 會在啟動時被展開,把 README 的內容直接注入到上下文裡。這適合那些 README 寫得特別好、但不想在 CLAUDE.md 裡重複一遍的專案。
/init 負責冷啟動。
新專案第一天,跑一下 /init,Claude 會掃描倉庫結構、分析依賴、讀 README,生成一份基礎的 CLAUDE.md。包含建置命令、測試命令、專案基本描述。
/memory 負責熱更新。
Claude Code 有一套自動記憶系統。每個專案在 ~/.claude/projects/ 目錄下都有一個 MEMORY.md 檔案,Claude 會把跨會話需要記住的資訊自動寫到這裡。下次啟動會話時,MEMORY.md 的前 200 行會被自動載入進上下文。

關鍵問題來了:什麼該寫在 CLAUDE.md,什麼該放在 memory 裡?
CLAUDE.md 放團隊共享的、長期穩定的規則。這些內容會提交到 git,所有人都能看到、都要遵守。比如建置命令、程式碼規範、架構約定。
memory 放個人的、會變化的、日常協作中累積的經驗。

實際維護的節奏是這樣的:
第一週,跑 /init 生成基礎版 CLAUDE.md。在日常使用中,Claude 會自動往 memory 裡累積經驗。
第二週開始,review 一遍 memory 裡的內容。發現有些經驗其實是通用規則,就把它提煉到 CLAUDE.md 裡。memory 裡過時的條目,主動清理掉。
CLAUDE.md 不是孤立存在的。Claude Code 的整個配置體系有四個角色,搞清楚各自的邊界很關鍵。
CLAUDE.md 管「建議」。
settings.json 管「強制」。 權限控制、環境變數、MCP 伺服器配置,這些放在 .claude/settings.json 裡。沒有商量餘地,硬性約束。
json 體驗 AI 程式碼助手 程式碼解讀複製代碼{
"permissions": {
"allow": ["Bash(npm run *)", "Bash(git *)"],
"deny": ["Bash(rm -rf *)", "Bash(git push --force)"]
}
}
hooks 管「自動化」。 如果某件事必須每次都執行,別在 CLAUDE.md 裡寫「請記得格式化程式碼」,直接配一個 hook,每次編輯檔案後自動跑 Prettier。hooks 由 harness 執行,不依賴 Claude 的「記憶」。
rules/ 管「精準投放」。 前面詳細講過了,按路徑限定載入規則,節省指令預算。

一句話記住它們的分工:CLAUDE.md 管建議,settings.json 管強制,hooks 管自動化,rules/ 管精準投放。
說了這麼多,給大家一份實戰模板,直接複製到專案裡改改就能用:
markdown 體驗 AI 程式碼助手 程式碼解讀複製代碼# CLAUDE.md
## Commands
- 建置:mvn clean package -DskipTests
- 測試:mvn test
- 單個測試:mvn test -Dtest=XxxTest
- 程式碼檢查:mvn spotbugs:check
- 格式化:mvn spotless:apply
## What This Is
一句話說清楚專案是什麼。
比如:PaiCLI 是一個純 Java 實現的 Agent CLI,28K 行程式碼,不依賴 Spring AI/LangGraph4J。
## Architecture
- 入口:Main.java → CliCommandParser 分發命令
- Agent 迴圈:AgentLoop.java,工具註冊在 ToolRegistry
- 記憶系統:MemoryManager,基於檔案持久化
- 不要動 agent/core/ 下的介面定義,下游工具全部依賴它們
## Things That Will Bite You
- search_code 是 RAG 輔助,不是主要程式碼定位方式,優先用 glob → grep → read
- 改了命令入口 → 必須同步 Main.java + CliCommandParser + 測試 + 文件
- FileUtils 的路徑處理已經做了沙箱限制,不要繞過它自己拼路徑
- 測試裡的 API Key 全部用 mock,禁止提交真實 Key
## Code Conventions
- 日誌用 SLF4J,不用 System.out
- 例外不要吞掉,至少 log.warn
- 所有 public API 回傳統一的 Result 包裝類
- 新工具必須實作 Tool 介面並在 ToolRegistry 註冊
## Don't
- 不要在業務程式碼裡直接 new Thread,用 ExecutorService
- 不要改 .env.example 的格式,CI 依賴它
- 不要往 CLAUDE.md 裡加「保持程式碼整潔」這種廢話
整個檔案不到 50 行,但該覆蓋的全覆蓋了。
Commands 讓 Claude 知道怎麼建置測試,Architecture 讓它知道程式碼在哪,Things That Will Bite You 防止它踩坑,Don't 劃出紅線。
沒有一句廢話。
回到開頭那個問題:「CLAUDE.md 你是怎麼維護的?」
如果面試官問到這個問題,三句話就夠了:
第一句講機制:CLAUDE.md 是 Claude Code 的持久化指令檔案,啟動時自動載入。它有四層載入體系,從系統級到子目錄級,越靠近工作目錄優先級越高。
第二句講原則:arXiv 論文實測,500 條指令密度下最強模型準確率只有 68%,指令越多遵循率越低。所以核心規則放 CLAUDE.md,控制在 80 行以內;按場景拆分的規則放 .claude/rules/ 目錄,用 paths 前置欄位做路徑限定,按需載入,避免所有規則擠在一個檔案裡互相淹沒。
第三句講實作:日常維護靠兩個命令——/init 冷啟動生成基礎版,/memory 自動累積跨會話經驗。好的規則只寫 Claude 自己推斷不出來的東西,定期 review,像維護程式碼一樣維護 CLAUDE.md。
好了,今天的內容就到這裡。你的 CLAUDE.md 寫了多少行?歡迎留言區分享~
我們下期見 👋