騰訊面試官問 CLAUDE.md 維護,我只說了兩個詞,他當場愣住了!!

大家好,我是二哥呀。

前兩天有個讀者私訊我,說騰訊二面問了這樣一個問題:「CLAUDE.md 你是怎麼維護的?」他知道的就是「/init 一下」,其他平常也沒怎麼關注。

這就好比面試官想讓你回答「資料庫怎麼優化的」,結果你回答了「裝個 MySQL 就行了」。

說了,但又好像沒說。

/init 只是起點。

CLAUDE.md 這個檔案,寫好了就是超級外掛,你的程式碼交付品質杠杠的。

寫爛了就是 token 噪音。今天這篇內容,給大家來一次硬核拆解。

系好安全帶,我們粗粗粗發~

01、CLAUDE.md 到底是個什麼東西

先把概念捋清楚。

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。這份自動生成的檔案通常包含:建置命令、測試命令、專案結構描述。

02、Claude Code 的四層載入體系

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.tsgetMemoryFiles() 函式裡:

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 互相矛盾。這是維護的第一原則。

03、LLM 的指令預算

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 的有效空間,真的沒有大家想像得那麼多。

論文來源:arxiv.org/abs/2507.11…

這就像給新員工寫入職須知。

寫 10 條,他能記住 8 條。寫 50 條,他能記住 20 條。寫 200 條,翻都不想翻,記住的可能還不到 10 條。

怎麼判斷一條指令該不該放進 CLAUDE.md?

問兩個問題:

第一,如果不寫這條,Claude 會不會搞錯?如果 Claude 靠讀程式碼就能推斷出來,別寫。

第二,這條指令是不是每次會話都需要?如果只在特定場景下需要,放到 rules/ 目錄裡做路徑限定(後面講),別塞在 CLAUDE.md 裡浪費預算。

Anthropic 官方給了一個非常實用的建議:像維護程式碼一樣維護 CLAUDE.md

定期 review,發現 Claude 沒遵守的指令,加「重要」或「務必要執行」強調;發現 Claude 本來就會做對的指令,果斷刪掉。

來源:www.anthropic.com/engineering…

04、什麼樣的規則會真正生效?

指令預算告訴我們 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 限制在專案根目錄,禁止絕對路徑逃逸和符號連結穿越」才是規則。

05、A 廠是怎麼寫 CLAUDE.md 的?

來看看 Anthropic 的專案是怎麼寫的。

他們的 claude-code-action 倉庫(就是 GitHub Actions 裡跑 Claude Code 的那個專案)有一份 CLAUDE.md,我去翻了一下:

  • Commands:建置、測試、lint 的具體命令
  • What This Is:專案是什麼,一句話說清楚
  • How It Runs:執行機制,不是文件式的介紹,而是「改程式碼之前必須知道的事」
  • Key Concepts:核心概念,3-5 個要點
  • Things That Will Bite You:踩坑清單,列出會讓人踩雷的細節
  • Code Conventions:程式碼約定,只寫和預設不一樣的部分

06、rules/ 目錄

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 裡重複一遍的專案。

07、/init 和 /memory

/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 裡過時的條目,主動清理掉。

08、Claude Code 配置體系

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/ 管精準投放。

09、一份拿來就能用的模板

說了這麼多,給大家一份實戰模板,直接複製到專案裡改改就能用:

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 劃出紅線。

沒有一句廢話。

10、面試怎麼回答?

回到開頭那個問題:「CLAUDE.md 你是怎麼維護的?」

如果面試官問到這個問題,三句話就夠了:

第一句講機制:CLAUDE.md 是 Claude Code 的持久化指令檔案,啟動時自動載入。它有四層載入體系,從系統級到子目錄級,越靠近工作目錄優先級越高。

第二句講原則:arXiv 論文實測,500 條指令密度下最強模型準確率只有 68%,指令越多遵循率越低。所以核心規則放 CLAUDE.md,控制在 80 行以內;按場景拆分的規則放 .claude/rules/ 目錄,用 paths 前置欄位做路徑限定,按需載入,避免所有規則擠在一個檔案裡互相淹沒。

第三句講實作:日常維護靠兩個命令——/init 冷啟動生成基礎版,/memory 自動累積跨會話經驗。好的規則只寫 Claude 自己推斷不出來的東西,定期 review,像維護程式碼一樣維護 CLAUDE.md。

好了,今天的內容就到這裡。你的 CLAUDE.md 寫了多少行?歡迎留言區分享~

我們下期見 👋


原文出處:https://juejin.cn/post/7645501139741327401


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝12   💬4   ❤️1
464
🥈
alicec
📝1   ❤️2
87
#4
我愛JS
💬1  
3
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登