Bạn dùng Claude Code một tuần và nó hoạt động ổn. Rồi bạn thêm vài dòng instruction vào CLAUDE.md. Một tháng sau, file đó dài 400 dòng, nhét đủ mọi thứ từ git convention đến cách đặt tên component React đến câu lệnh deploy production. Mỗi session, toàn bộ 400 dòng đó đều được nạp vào prompt, dù bạn chỉ đang sửa một file Python nhỏ.

Đây là anti-pattern phổ biến nhất với CLAUDE.md, và nó có cái giá thực: token lãng phí, noise trong instruction, và khả năng bảo trì kém. Bài này trình bày kiến trúc hierarchical mà CC hỗ trợ để tránh đúng cái vấn đề đó.

Recap từ bài 1: CLAUDE.md là L2

Trong bài 1, ta đã thấy prompt mỗi turn được dựng từ 5 lớp. L2 là “User instructions”, bao gồm CLAUDE.md và rules. Đây là lớp bạn kiểm soát nhiều nhất.

+----------------------------------+
|  L5: User message + history     |
+----------------------------------+
|  L4: Auto-context (memory, ...)  |
+----------------------------------+
|  L3: Tools available             |
+----------------------------------+
|  L2: User instructions           |  <- CLAUDE.md + rules ở đây
|       (CLAUDE.md + rules)        |
+----------------------------------+
|  L1: System prompt               |
+----------------------------------+

L1 bạn không sửa được. L2 là nơi phần lớn customize xảy ra. Bài này đi sâu vào cách L2 được tổ chức.

Ba mức instruction

CC hỗ trợ ba mức, mỗi mức có scope và lifecycle khác nhau:

~/.claude/CLAUDE.md          <- mức 1: global, mọi session
<project>/.claude/CLAUDE.md  <- mức 2: project-scoped
~/.claude/rules/*.md         <- mức 3: modular, có thể path-scoped

Mức 1: ~/.claude/CLAUDE.md (global)

File duy nhất. Auto-loaded cho mọi session, bất kể bạn đang ở thư mục nào.

Đây là nơi đặt convention cá nhân, áp dụng cho tất cả project của bạn. Ví dụ:

  • Quy tắc commit format (feat(ticket): description, imperative form)
  • Convention về .local/ folder cho credential
  • Quy tắc git (không auto-push, không commit khi chưa được nói)
  • Convention về ngôn ngữ trả lời (Vietnamese)
  • Danh sách project và context chung

Global CLAUDE.md cũng là chỗ ghi “topology” của setup CC của bạn: có những repo nào, workflow nào đặc biệt, những gotcha cần nhắc. Trong một setup thực tế, global CLAUDE.md có thể có section về git workflow cho từng loại repo, section về memory submodule nếu dùng, và section về cách tổ chức skill/hook.

Mức 2: <project>/.claude/CLAUDE.md (project-scoped)

Khi cwd nằm trong project, CC tự động load file CLAUDE.md trong thư mục .claude/ của project đó. File này stack lên trên global: cả hai đều được include vào L2.

Đây là nơi đặt convention riêng của project:

## Build

npm run dev          # local dev server
npm run build        # production build
npm run test         # run test suite

## Quan trọng

- KHÔNG sửa /src/api/endpoints.ts trừ khi được yêu cầu rõ
- Test file đặt cạnh source file, không có folder test/ riêng
- Tất cả component mới đặt trong src/components/<feature>/

Project CLAUDE.md không đi vào global. Nếu bạn mở một project khác, convention project A không bị nhét vào session project B.

Lưu ý: một số codebase cũ hơn đặt CLAUDE.md ở root project thay vì trong .claude/. CC đọc được cả hai, nhưng convention hiện tại khuyên dùng <project>/.claude/CLAUDE.md để tách khỏi root và dễ gitignore nếu cần.

Mức 3: ~/.claude/rules/*.md (modular)

Thay vì một global CLAUDE.md ngày càng phình to, bạn chia instruction thành nhiều file nhỏ trong ~/.claude/rules/. Mỗi file là một topic.

Ví dụ một setup thực tế:

~/.claude/rules/
├── git.md                  # git convention, commit format
├── safety.md               # các rule an toàn (không auto-push, ...)
├── code-style.md           # file naming, code quality
├── development.md          # không dùng mock data, reuse endpoint
├── agent-safety.md         # rule khi spawn agent/worktree
├── user-local-folder.md    # convention .local/ folder
├── user-shell-zsh.md       # zsh-specific shell rules
└── frontend/
    ├── react-code-style.md
    ├── react-component-structure.md
    ├── react-hooks.md
    └── react-imports.md

Mỗi file nhỏ, tập trung một chủ đề. Dễ tìm, dễ sửa, dễ bật/tắt.

Path-scoping: progressive context

Đây là tính năng mạnh nhất của modular rules.

Rule file có thể có YAML frontmatter với key paths:

---
paths: **/*.tsx
---

# React Component Structure

Components must follow this exact structure from top to bottom:
...

Rule có paths chỉ được load khi CC đang thao tác với file match pattern đó. Nếu bạn đang edit một file Python, rule **/*.tsx không được nạp vào prompt. Nếu bạn mở file .tsx, nó vào.

Một ví dụ thực tế: setup có bốn file rule React, tất cả đều có paths: **/*.{ts,tsx}:

# react-code-style.md
---
paths: **/*.{ts,tsx}
---
# react-component-structure.md
---
paths: **/*.tsx
---
# react-hooks.md
---
paths: **/*.tsx
---
# react-imports.md
---
paths: **/*.{ts,tsx}
---

Khi bạn đang sửa một file Bash script, không có dòng nào trong bốn file này vào prompt. Khi bạn mở UserProfile.tsx, toàn bộ bốn file được nạp.

Đây là cơ chế progressive context: prompt chỉ chứa instruction phù hợp với việc đang làm. Token tiết kiệm, model ít bị nhiễu bởi instruction không liên quan.

Glob pattern hỗ trợ chuẩn: **/*.tsx, src/api/**/*.ts, **/migrations/*.sql. Rule không có paths thì luôn được load (global rule).

Stacking: cả ba mức cùng lúc

Trong một session điển hình:

Session: đang edit src/components/UserCard.tsx trong project "myapp"

L2 bao gồm:
  [1] ~/.claude/CLAUDE.md                           (global, always)
  [2] myapp/.claude/CLAUDE.md                       (project, vì cwd trong myapp)
  [3] ~/.claude/rules/git.md                        (global rule, không có paths)
  [4] ~/.claude/rules/safety.md                     (global rule, không có paths)
  [5] ~/.claude/rules/frontend/react-hooks.md       (paths: **/*.tsx match)
  [6] ~/.claude/rules/frontend/react-imports.md     (paths: **/*.{ts,tsx} match)
  
  KHÔNG có:
  [x] ~/.claude/rules/user-shell-zsh.md             (global rule, luôn load)
  -- thực ra file này không có paths, nên nó luôn load
  [x] ~/.claude/rules/frontend/react-code-style.md  (cũng load vì paths: **/*.{ts,tsx})

Tất cả stack lên thành một khối instruction liền mạch. Model thấy chúng như một instruction dài, không phân biệt đến từ file nào.

Khi nào dùng cái nào

Bạn muốn…Đặt ở đâu
Convention cá nhân áp dụng cho mọi project~/.claude/CLAUDE.md global
Build / test / deploy command của project cụ thể<project>/.claude/CLAUDE.md
Convention theo công nghệ (React, Python, SQL)~/.claude/rules/frontend/*.md với paths:
Rule an toàn cần áp dụng mọi lúc~/.claude/rules/safety.md (không có paths:)
Convention phức tạp chỉ cần trong một project<project>/.claude/rules/ (project-local rules)
Instruction quá dài, muốn tách thành nhiều file~/.claude/rules/*.md modular, không cần paths:

Anti-pattern: CLAUDE.md monolithic

Cái bẫy phổ biến: nhồi mọi thứ vào một CLAUDE.md duy nhất.

# CLAUDE.md (400 dòng, nhồi tất cả)

## Git Convention
...50 dòng...

## React Rules
...80 dòng...

## Python Rules
...60 dòng...

## Build Commands cho project A
...
## Build Commands cho project B
...
## Deployment Notes
...
## Persona
...

Vấn đề:

  • Mỗi session, toàn bộ 400 dòng được nạp dù chỉ đang sửa một file Python nhỏ. React rules, deployment notes của project khác, tất cả đều vào prompt.
  • Khi muốn sửa convention, phải tìm trong một file dài không rõ convention nào còn áp dụng.
  • Không có cách tắt một rule khi không cần mà không xóa khỏi file.
  • Build command của project A lẫn với project B trong cùng một file.

Kiến trúc hierarchical giải quyết tất cả: global CLAUDE.md ngắn và tập trung vào convention thực sự global, project CLAUDE.md chứa đúng config của project đó, rules modular tách theo topic và path-scope khi hợp lý.

Một setup điển hình sau khi tái cấu trúc

Trước khi tái cấu trúc, một setup có thể trông như này:

~/.claude/CLAUDE.md    <- 380 dòng, nhồi tất cả

Sau khi tái cấu trúc:

~/.claude/CLAUDE.md                          <- 80 dòng, chỉ còn global context
~/.claude/rules/git.md                       <- 40 dòng
~/.claude/rules/safety.md                    <- 25 dòng
~/.claude/rules/code-style.md                <- 20 dòng
~/.claude/rules/development.md               <- 15 dòng
~/.claude/rules/frontend/react-hooks.md      <- 20 dòng (paths: **/*.tsx)
~/.claude/rules/frontend/react-imports.md    <- 15 dòng (paths: **/*.{ts,tsx})

project-a/.claude/CLAUDE.md                  <- 30 dòng
project-b/.claude/CLAUDE.md                  <- 25 dòng

Tổng instruction không giảm. Nhưng mỗi session chỉ load phần liên quan. Session edit Python trong project A không thấy React rules. Session edit .tsx trong project B không thấy build command của project A.

Thứ tự ưu tiên khi có xung đột

Khi global CLAUDE.md và project CLAUDE.md có instruction trái ngược nhau, CC ưu tiên instruction nào đến sau trong stack. Thực tế: project-level thường override global vì được nạp sau.

Ví dụ:

# ~/.claude/CLAUDE.md (global)
Trả lời bằng tiếng Việt.

# myapp/.claude/CLAUDE.md (project)
This project uses English. Respond in English only.

Khi đang làm việc trong myapp, instruction tiếng Anh sẽ thắng. Đây là hành vi có chủ đích: project-level có thể override global cho một project cụ thể mà không ảnh hưởng setup của project khác.

Giới hạn của CLAUDE.md và rules

CLAUDE.md và rules là instruction tĩnh. Chúng được nạp vào prompt nhưng không thể “chạy code” hay trigger một hành động khi event xảy ra.

Hai nhu cầu mà CLAUDE.md không đáp ứng được:

  • Trigger tự động theo sự kiện: ví dụ “sau khi CC trả lời xong, gửi notification về Telegram”. CLAUDE.md không thể làm điều này. Đây là việc của hook (bài 9).
  • Workflow phức tạp cần invoke qua lệnh: ví dụ /deploy, /review-pr. Đây là việc của skill (bài 7).

Nói cách khác: CLAUDE.md là nơi đặt “rule nên biết mọi lúc”, còn skill là “workflow chạy khi được gọi”, hook là “hành động tự động theo event”.

Tóm tắt và bài tiếp theo

  • L2 của prompt stack (User instructions) có ba mức: global ~/.claude/CLAUDE.md, project <project>/.claude/CLAUDE.md, và rule modular trong ~/.claude/rules/.
  • Global và project stack lên nhau, cả hai được load cùng lúc khi cwd trong project.
  • Rule modular có thể có frontmatter paths: để chỉ load khi đang thao tác với file match pattern. Đây là cơ chế progressive context.
  • Anti-pattern cần tránh: nhồi mọi thứ vào một CLAUDE.md monolithic. Kết quả là prompt bị polluted mỗi session dù instruction không liên quan.
  • Khi nào dùng cái gì: global cho convention cá nhân, project cho config của project, rule modular cho convention theo công nghệ hoặc path.

Bài 7 sẽ đến với skills: cách viết một workflow phức tạp dưới dạng slash command có thể gọi khi cần, và điểm khác biệt giữa skill với rule CLAUDE.md. Bài 9 sẽ đến hooks: khi rule trong CLAUDE.md không đủ và cần trigger hành động theo event.


Bài thuộc series Claude Code từ zero. Series plan tại bài giới thiệu.