Bỏ qua để đến nội dung

3.1 CLAUDE.md Hierarchy, Scoping, and Modular Organisation

Claude Code đọc cấu hình từ file CLAUDE.md ở ba cấp. Biết cấp nào áp dụng ở đâu — và nhận ra khi ai đó đặt nhầm cấp — là thứ đề thi hỏi đi hỏi lại.

User-level: ~/.claude/CLAUDE.md

File này chỉ áp dụng cho riêng bạn. Nó nằm trong home directory, ngoài mọi repository, nên không được version-control và không bao giờ đi theo git. Một đồng nghiệp mới clone repo sẽ không nhận được gì từ đây. Cấp này chỉ nên chứa sở thích cá nhân thuần tuý: mức độ chi tiết của output, output style ưa dùng, mấy shortcut riêng của bạn.

Project-level: .claude/CLAUDE.md hoặc CLAUDE.md ở root

File này áp dụng cho mọi người trong project. Nó nằm trong repository và được version-control, nên ai clone hay pull repo cũng tự động nhận được. Chuẩn chung của team thuộc về chỗ này: naming convention, pattern xử lý lỗi, yêu cầu testing, quyết định kiến trúc, checklist code review.

Cả .claude/CLAUDE.md (trong thư mục .claude) lẫn CLAUDE.md ở root repository đều là vị trí project-level hợp lệ. Đề thi có thể đưa ra một trong hai đường dẫn.

Directory-level: file CLAUDE.md trong thư mục con

Những file này có hiệu lực khi bạn đang làm việc trong đúng thư mục đó. Dùng cho convention riêng của từng package, khác với root. Ví dụ /packages/api/CLAUDE.md có thể chứa convention REST mà package frontend không bao giờ cần.

CLAUDE.md không phải loại config có precedence nghiêm ngặt. Tài liệu memory của Anthropic nói rõ: “All discovered files are concatenated into context rather than overriding each other.” Mọi file phù hợp đều được load vào cùng một context window. Không file nào thay thế file nào.

Tài liệu mô tả một load order có ghi chép, chứ không phải chuỗi precedence:

  1. File được sắp xếp từ phạm vi rộng nhất tới cụ thể nhất. Instruction cấp project xuất hiện trong context sau instruction cấp user. Theo cây thư mục, “content is ordered from the filesystem root down to your working directory”, nên “instructions closer to where you launched Claude are read last”.
  2. Trong cùng một thư mục, CLAUDE.local.md được nối sau CLAUDE.md, nên ghi chú cá nhân của bạn là thứ cuối cùng Claude đọc ở cấp đó.

Không điểm nào ở trên biến nó thành hierarchy kiểu được ăn cả. Tài liệu nói thẳng: “if two rules contradict each other, Claude may pick one arbitrarily.” CLAUDE.md được đưa vào dưới dạng user message — không phải một phần của system prompt — và Anthropic nói “there’s no guarantee of strict compliance”. Hãy coi CLAUDE.md là hướng dẫn mà model thường tuân theo, không phải một lớp cấu hình có override tất định.

Hệ quả thực tế: nếu một quy tắc bắt buộc phải đúng ở mọi lần chạy — chặn một tool, ép một formatter, một permission policy — đừng trông vào scoping của CLAUDE.md để cưỡng chế. Hãy mã hoá nó trong settings.json (client cưỡng chế bất kể Claude quyết định gì) hoặc trong một hook (kích hoạt tại một lifecycle event cố định). Tài liệu Anthropic ghi thẳng: “Settings rules are enforced by the client regardless of what Claude decides to do. CLAUDE.md instructions shape Claude’s behavior but are not a hard enforcement layer.”

Vượt vài trăm dòng, một file CLAUDE.md duy nhất trở nên cực khó bảo trì. Cú pháp @ cho phép tách nó ra nhiều file và tham chiếu từ file chính. Directive chỉ đơn giản là @ theo sau bởi một path. Không có keyword @import, dù nửa số tài liệu bạn tìm thấy trên mạng viết như vậy.

Cú pháp trong CLAUDE.md của bạn:

.claude/CLAUDE.md
Coding standards:
@./standards/naming-conventions.md
@./standards/error-handling.md
@./standards/testing-requirements.md

Mỗi dòng @<path> khiến file đó được inline vào CLAUDE.md tại thời điểm load. CLAUDE.md của từng package chỉ import những chuẩn liên quan tới nó. Package API kéo vào convention API, frontend kéo vào rule component. Không trùng lặp.

Có một điểm tài liệu không nói: import load eager. File được tham chiếu bị inline ngay khi Claude đọc CLAUDE.md, y như bạn dán tay vào. Nên tách một CLAUDE.md 600 dòng thành sáu import 100 dòng giúp source dễ làm việc hơn, nhưng context Claude thực sự thấy vẫn nguyên kích thước đó. Muốn giảm context mỗi session thì công cụ đúng là .claude/rules/ với frontmatter path-scoped (nói kỹ ở Task Statement 3.3). Những file đó chỉ load khi Claude đang làm việc trên path khớp.

CLAUDE.local.md, override chỉ có ở máy bạn

Phần tiêu đề “CLAUDE.local.md, override chỉ có ở máy bạn”

CLAUDE.local.md nằm cạnh CLAUDE.md ở bất kỳ cấp nào trong hierarchy và load theo cùng cách, với ba khác biệt nhỏ đáng biết:

  • Thứ tự load. CLAUDE.local.md được nối sau CLAUDE.md ở cùng cấp, nên ghi chú cá nhân của bạn là thứ cuối cùng Claude đọc ở đó. Đó là load order, không phải precedence: đọc sau không có nghĩa là thắng khi mâu thuẫn. Nếu hai instruction chọi nhau, Claude vẫn có thể chọn bất kỳ cái nào.
  • Gitignore theo quy ước. Hậu tố .local đánh dấu những file bạn không muốn commit. Đa số team thêm CLAUDE.local.md vào .gitignore để tinh chỉnh cá nhân ở yên chỗ cá nhân.
  • Dùng để làm gì. CLAUDE.md chung là rule của team. CLAUDE.local.md bên cạnh là những thói quen riêng của bạn trong repo này: một path scratchpad ưa thích, một đoạn giải thích dài bạn cứ phải dán lại, một ghi chú debug tạm mà tuần sau sẽ xoá.

Hãy coi CLAUDE.local.md như phiên bản giới hạn trong project của ~/.claude/CLAUDE.md: cùng ý tưởng, phạm vi hẹp hơn. Nếu bạn thấy mình đang dùng nó để viết một rule của team, rule đó thuộc về CLAUDE.md.

Thay cho một file CLAUDE.md duy nhất, thư mục .claude/rules/ chứa các file rule theo chủ đề:

  • testing.md — đặt tên test, pattern assertion, cách dùng fixture
  • api-conventions.md — đặt tên endpoint, schema request/response
  • deployment.md — checklist deploy, cấu hình môi trường

Mỗi file có thể kèm YAML frontmatter với path scoping (chi tiết ở Task Statement 3.3). Không có frontmatter thì file rule load cho mọi session.

Chẩn đoán xem cái gì đã load: /memory và /context

Phần tiêu đề “Chẩn đoán xem cái gì đã load: /memory và /context”

Khi hành vi trôi dạt giữa các session, hoặc giữa các developer, bạn cần thấy session thực sự nhặt được những memory file nào. Nếu Claude Code tuân thủ convention của team với người này mà lờ đi với người kia, danh sách đó giải quyết luôn câu hỏi.

Claude Code hiện tại chia việc này cho hai lệnh. /memory liệt kê các vị trí CLAUDE.md, CLAUDE.local.md và auto-memory, đồng thời mở chúng trong editor. /context báo cáo cái gì thực sự đã load vào session này, dưới mục Memory files — nên muốn xác nhận một file đang sống thì chạy /context và đọc danh sách đó. Tài liệu nói rõ: “check the list under Memory files to verify your CLAUDE.md and CLAUDE.local.md files loaded”. (Tài liệu memory của Claude Code, đã đối chiếu tháng 8/2026.)

Khi /compact tóm tắt một session dài, CLAUDE.md ở project root quay lại nguyên vẹn. Không phải vì nó nằm ở chỗ đặc quyền nào. Mà vì Claude đọc lại nó từ disk sau khi compaction rồi inject lại, và instruction của bạn vốn chưa bao giờ nằm trong lịch sử hội thoại, nên chẳng có gì ở đó để bộ tóm tắt nén.

Hai thứ không tự quay lại: file CLAUDE.md lồng trong thư mục con, và file .claude/rules/ có frontmatter paths:. Cả hai load theo nhu cầu, nên chúng trở lại vào lần tiếp theo Claude đọc một file khớp, chứ không phải ngay khi compaction kết thúc. Khi một instruction có vẻ biến mất sau /compact, thường là vì lý do đó. Khả năng còn lại là instruction vốn chỉ tồn tại trong hội thoại, mà hội thoại thì compaction được phép tóm tắt.

Tình huống thi trọng yếu: người mới vào team không nhận được instruction

Phần tiêu đề “Tình huống thi trọng yếu: người mới vào team không nhận được instruction”

Đây là cái bẫy ruột của đề thi cho Task Statement 3.1. Nó thường diễn ra như sau:

Developer A đã ở trong team nhiều tháng. Claude Code tuân thủ hoàn hảo mọi convention của team — đặt tên API, cấu trúc test, xử lý lỗi. Developer B vào team, clone repository, và Claude Code cho ra kết quả không nhất quán, bỏ qua các convention.

Nguyên nhân gốc luôn giống nhau: convention nằm trong config user-level của Developer A (~/.claude/CLAUDE.md) thay vì config project-level (.claude/CLAUDE.md hoặc CLAUDE.md ở root). Config user-level không được chia sẻ qua git. Developer B chưa bao giờ nhận được những instruction đó.

Cách sửa: chuyển instruction từ cấu hình user-level sang project-level.

Bạn cần nhận ra tình huống này ngay từ cái nhìn đầu. Thấy “người mới vào team” đi kèm “hành vi không nhất quán”? Kiểm tra xem cấu hình đang nằm ở đâu.

Claude Code của Developer A tuân thủ hoàn hảo naming convention API của team. Developer B, vừa vào tuần trước, nhận được cách đặt tên không nhất quán từ Claude Code. Cả hai làm trên cùng repo và cùng branch. Nguyên nhân gốc nhiều khả năng nhất là gì?

  • A. Developer B chưa chạy /memory để load file cấu hình vào session, nên instruction cấp project chưa bao giờ vào được context của model
  • B. Naming convention API được lưu trong CLAUDE.md user-level của Developer A (~/.claude/CLAUDE.md) thay vì cấu hình cấp project
  • C. Developer B chưa cài MCP server cung cấp rule naming convention của team cho session Claude Code chạy trên máy họ
  • D. Convention nằm trong một file .claude/rules/ mà setup local của Developer B không hỗ trợ, nên rule không bao giờ load trên máy họ
Đáp án & giải thích

Đúng: B

  • A — /memory là công cụ chẩn đoán, cho biết file nào đã load. Nó không kích hoạt việc load. Cấu hình tự load dựa trên vị trí file.
  • B — CLAUDE.md user-level không được version-control hay chia sẻ qua git. Developer A có instruction ở local từ nhiều tháng dùng; Developer B vừa clone repo nên không có. Chuyển instruction sang .claude/CLAUDE.md cấp project là xong.
  • C — MCP server cung cấp tích hợp tool, không phải cấu hình CLAUDE.md. Naming convention là cấu hình, không phải tool.
  • D — File .claude/rules/ nằm trong repository và được version-control. Nếu convention ở đó, Developer B sẽ nhận được khi clone, y như mọi file version-control khác.

Năm câu trắc nghiệm theo format đề thi về CLAUDE.md Hierarchy, Scoping, and Modular Organisation. Chọn đáp án trước, rồi mở phần giải thích.

Claude Code của Developer A tuân thủ hoàn hảo naming convention API của team. Developer B, vừa vào tuần trước, nhận được cách đặt tên không nhất quán từ Claude Code. Cả hai làm trên cùng repo và cùng branch. Nguyên nhân gốc nhiều khả năng nhất là gì?

  • A. Developer B chưa chạy /memory để load các file cấu hình
  • B. Developer B cần cài một MCP server để truy cập rule naming convention
  • C. Convention nằm trong CLAUDE.md user-level của Developer A, không phải config của project
  • D. Convention nằm trong file .claude/rules/ mà hệ thống của Developer B không hỗ trợ
Đáp án & giải thích

Đúng: C

  • A sai vì: /memory là công cụ chẩn đoán, cho biết file nào đã load. Nó không kích hoạt việc load. Cấu hình tự load dựa trên vị trí file.
  • B sai vì: MCP server cung cấp tích hợp tool, không phải cấu hình CLAUDE.md. Naming convention là cấu hình, không phải tool.
  • C đúng vì: CLAUDE.md user-level (~/.claude/CLAUDE.md) không được version-control hay chia sẻ qua git. Developer A có instruction ở local; Developer B vừa clone repo nên không có. Chuyển instruction sang config cấp project (.claude/CLAUDE.md) là xử lý được.
  • D sai vì: File .claude/rules/ nằm trong repository và được version-control. Nếu convention ở đó, Developer B sẽ nhận được khi clone.

Project của một team đã phình lên hơn 200 dòng coding convention trong một file CLAUDE.md duy nhất. File bao gồm naming, testing, deployment, API design và infrastructure. Cách tổ chức lại nào dễ bảo trì nhất?

  • A. Dùng thư mục .claude/rules/ với các file rule theo chủ đề (testing.md, api-conventions.md, deployment.md)
  • B. Chuyển toàn bộ convention sang ~/.claude/CLAUDE.md user-level để mỗi developer tự tuỳ chỉnh
  • C. Tách file thành nhiều file CLAUDE.md trong thư mục root
  • D. Tạo một skill trong .claude/skills/ để load convention theo nhu cầu
Đáp án & giải thích

Đúng: A

  • A đúng vì: Thư mục .claude/rules/ chứa các file rule theo chủ đề. Mỗi file lo một mối quan tâm (testing, API, deployment). Đây chính là giải pháp thay thế được thiết kế cho một CLAUDE.md nguyên khối.
  • B sai vì: Config user-level là cá nhân và không chia sẻ qua git. Chuyển convention của team sang đó đồng nghĩa người mới vào team không nhận được gì.
  • C sai vì: Bạn không thể có nhiều file CLAUDE.md trong cùng một thư mục root. Thư mục .claude/rules/ mới là cơ chế được thiết kế để tách convention theo chủ đề.
  • D sai vì: Skill load theo nhu cầu như một workflow dạng tác vụ, hoặc khi bạn gọi bằng /name hoặc khi model tự nhặt từ context. Coding convention phải áp dụng cho mọi lần edit mà không phụ thuộc vào việc gọi, và đó là thứ .claude/rules/ cùng CLAUDE.md cung cấp.

Một developer chạy /memory và phát hiện một file rule đáng lẽ phải load thì lại không hiện ra. Điều đó nói lên gì?

  • A. Họ cần chạy lại /memory với cờ –reload để ép load
  • B. Họ cần restart Claude Code vì cấu hình chỉ load lúc khởi động
  • C. Lệnh /memory chỉ có ở interactive mode và không hoạt động trong session hiện tại của họ
  • D. Sai path, frontmatter hỏng, hoặc glob pattern không bao giờ khớp
Đáp án & giải thích

Đúng: D

  • A sai vì: /memory không có cờ –reload. Nó chỉ là công cụ chẩn đoán.
  • B sai vì: Cấu hình load dựa trên file đang được edit và hierarchy, không chỉ lúc khởi động.
  • C sai vì: /memory hoạt động trong session interactive để hiển thị cấu hình đã load.
  • D đúng vì: Nếu một file rule không load, vấn đề hoặc là file không tồn tại, hoặc frontmatter sai định dạng, hoặc (với rule path-scoped) developer không edit file nào khớp glob pattern.

Một project có packages/api/ với convention riêng cho API và packages/frontend/ với convention cho component. Team muốn mỗi package tuân theo chuẩn riêng mà không nhân bản phần convention dùng chung. Cách cấu hình đúng là gì?

  • A. Tạo CLAUDE.md cấp thư mục trong mỗi package, chứa toàn bộ convention kể cả phần dùng chung
  • B. CLAUDE.md cấp project với @ path import cho chuẩn dùng chung, cấp thư mục cho phần riêng của package
  • C. Đặt toàn bộ convention vào config user-level và bảo mỗi developer tự copy phần phù hợp
  • D. Tạo một skill cho mỗi package mà developer phải gọi trước khi làm việc trong thư mục đó
Đáp án & giải thích

Đúng: B

  • A sai vì: Cách này nhân bản convention dùng chung vào mọi thư mục package, tạo gánh nặng bảo trì và nguy cơ trôi dạt.
  • B đúng vì: @ path import cho phép tổ chức module hoá. Chú ý cú pháp: một dòng ghi @./standards/naming.md, không phải @import. Không có keyword @import. Chuẩn dùng chung nằm một chỗ và được import vào nơi cần. Convention riêng của package nằm trong file CLAUDE.md cấp thư mục. Không trùng lặp.
  • C sai vì: Config user-level là cá nhân và không chia sẻ qua git. Cách này hỏng ngay với người mới vào team.
  • D sai vì: Skill load theo nhu cầu như workflow dạng tác vụ. Convention phải áp dụng cho mọi lần edit như context luôn hiện diện, và đó là thứ CLAUDE.md (cùng .claude/rules/ cho trường hợp path-scoped) cung cấp.

Đâu là vị trí CLAUDE.md cấp project hợp lệ?

  • A. ~/CLAUDE.md
  • B. node_modules/.claude/CLAUDE.md
  • C. /etc/claude/CLAUDE.md
  • D. .claude/CLAUDE.md trong root của repository
Đáp án & giải thích

Đúng: D

  • A sai vì: ~/CLAUDE.md nằm trong home directory của user, không nằm trong repository. Nó cũng khác ~/.claude/CLAUDE.md (user-level) và sẽ không được nhận diện.
  • B sai vì: node_modules/ dành cho dependency đã cài. Cấu hình bên trong node_modules không được Claude Code nhận diện.
  • C sai vì: /etc/ là thư mục hệ thống. Claude Code không đọc cấu hình từ đường dẫn cấp hệ thống.
  • D đúng vì: .claude/CLAUDE.md trong repository là một trong hai vị trí project-level hợp lệ (cái còn lại là file CLAUDE.md ở root).