blog
Viết CLAUDE.md đúng chuẩn: bốn nhóm quy tắc và giới hạn thật của Claude Code
CLAUDE.md hoạt động tốt khi chia thành bốn nhóm rõ ràng: lệnh, quy tắc, kiến trúc theo path, và cách mở rộng khi dự án lớn lên. Hướng dẫn viết từng nhóm đúng giới hạn kỹ thuật thật của Claude Code, kèm ví dụ chạy được.
CLAUDE.md không phải chỗ chép mọi thứ agent "nên biết". Nó có giới hạn kỹ thuật thật, và một ranh giới rõ với hai cơ chế khác của Claude Code: .claude/rules/ cho quy tắc theo path, hook cho thứ phải chặn tuyệt đối. Chia đúng bốn nhóm dưới đây, CLAUDE.md ngắn lại mà Claude theo sát hơn.
Nhóm Commands: liệt lệnh Claude không tự đoán được
Claude đọc code rất tốt, nhưng không đoán được lệnh test riêng của dự án, cờ build đặc thù, hay biến môi trường bắt buộc. Nhóm Commands chỉ nên chứa đúng những thứ đó: lệnh build, lệnh test, lệnh lint, quirk môi trường dev. Bỏ mọi thứ Claude tự suy ra được từ package.json hay code.
Giữ file ngắn quan trọng hơn cảm giác đầy đủ. Docs Claude Code đặt giới hạn cụ thể: "target under 200 lines per CLAUDE.md file. Longer files consume more context and reduce adherence." Vượt 200 dòng thì tách bớt ra .claude/rules/ theo path thay vì nhét chung một file.
Cần kéo thêm ngữ cảnh mà không muốn phình CLAUDE.md, dùng cú pháp @path/to/file để import:
See @README.md for tổng quan dự án, @package.json cho lệnh npm sẵn có.
# Additional Instructions
- Git workflow: @docs/git-instructions.md
Import lồng được nhưng có giới hạn cứng: tối đa bốn tầng, "a maximum depth of four hops". Đừng xây chuỗi import sâu hơn, tầng thứ năm sẽ không nạp.
Một người dùng Claude Code viết lại kinh nghiệm sau hơn 1000 session kể cắt CLAUDE.md dự án từ 471 dòng xuống 61 dòng, và agent theo dõi nhất quán hơn chứ không kém đi. Muốn biết một dòng có đáng giữ không, tự hỏi câu docs gợi ý: "would removing this cause Claude to make mistakes?". Không thì cắt.
Viết Rules kiểu verify được, bỏ câu như "viết code sạch"
Đây là nhóm dễ viết sai nhất vì câu nào cũng nghe hợp lý lúc gõ. Quy tắc chỉ có tác dụng khi kiểm tra được Claude có làm đúng hay không:
| Viết thế này | Không viết thế này |
|---|---|
| Dùng 2 dấu cách để thụt lề | Format code cho gọn |
Chạy npm test trước khi commit | Nhớ test kỹ trước khi commit |
Handler API nằm ở src/api/handlers/ | Giữ code có tổ chức |
Docs gọi đúng tên vấn đề: câu như "self-evident practices like 'write clean code'" nên loại khỏi CLAUDE.md, vì không có cách nào biết Claude có tuân theo hay không. Một câu không verify được thì chỉ chiếm chỗ, không đổi hành vi.
Bảng nên/không nên đầy đủ hơn từ chính docs:
| Nên đưa vào CLAUDE.md | Nên bỏ ra |
|---|---|
| Lệnh bash Claude không đoán được | Thứ Claude tự đọc code cũng biết |
| Quy ước code style khác mặc định | Quy ước ngôn ngữ chuẩn Claude đã biết |
| Lệnh test, test runner ưu tiên dùng | Tài liệu API chi tiết (dẫn link thay vì dán) |
| Quirk môi trường dev (biến env bắt buộc) | Thông tin đổi thường xuyên |
Một quy tắc hay bị bỏ qua, viết hoa IMPORTANT hoặc YOU MUST để tăng khả năng Claude tuân theo. Không đảm bảo tuyệt đối, nhưng đáng thử trước khi nghi ngờ Claude "không nghe lời".
Tách Architecture theo path bằng .claude/rules/
Một CLAUDE.md gộp hết quy tắc frontend, backend, content vào một file thì sớm muộn cũng vượt 200 dòng. Cách tách đúng là thư mục .claude/rules/: mỗi file khai paths bằng glob trong frontmatter, chỉ nạp vào context khi Claude đọc file khớp pattern đó.
---
paths:
- "src/api/**/*.ts"
---
# API Development Rules
- Mọi endpoint phải có input validation
- Dùng format response lỗi chuẩn
Rule không khai paths thì nạp mọi session, ngang hàng CLAUDE.md. Cơ chế glob có giới hạn cứng đáng biết trước khi viết pattern phức tạp: toàn bộ danh sách paths của một rule chia sẻ ngân sách 1.000 pattern mở rộng và 4 MiB, brace expansion kiểu {ts,tsx} tính nhân vào ngân sách đó.
.claude/rules/ còn hỗ trợ symlink để dùng chung một bộ rule giữa nhiều repo, và có bản user-level ở ~/.claude/rules/ áp cho mọi project trên máy, load trước rule của project.
Repo đang chạy bài này tách ba rule theo path thay vì gộp vào một CLAUDE.md: một rule chỉ nạp khi đụng components/**, layouts/**, styles/**; một rule khác chỉ nạp khi đụng content/**. Mỗi session chỉ nạp đúng phần liên quan tới file đang sửa, CLAUDE.md gốc giữ được dưới 200 dòng.
Biết khi nào cần hook thay vì CLAUDE.md
CLAUDE.md và .claude/rules/ đều được nạp như context, Claude đọc và cố tuân theo. Docs nói thẳng giới hạn đó: "there's no guarantee of strict compliance". Muốn một hành động xảy ra mọi lần không có ngoại lệ, viết CLAUDE.md không đủ.
Hook là công cụ đúng cho trường hợp đó: shell command Claude Code chạy tại điểm cố định trong vòng đời, "certain actions always happen rather than relying on the LLM to choose to run them". Muốn chặn tuyệt đối một hành động, ví dụ ghi vào thư mục migration, dùng PreToolUse hook thay vì thêm một dòng CLAUDE.md rồi hy vọng.
Ngay cả hook cũng có giới hạn thực tế đáng biết: một Stop hook có thể chặn Claude kết thúc lượt cho tới khi check pass, nhưng Claude Code tự động ghi đè hook và kết thúc lượt sau 8 lần chặn liên tiếp, để tránh vòng lặp treo vô hạn.
Chọn đúng công cụ cho đúng loại quy tắc:
- Có thể verify bằng mắt, không bắt buộc tuyệt đối (style, quy ước đặt tên) → CLAUDE.md hoặc
.claude/rules/ - Chỉ áp cho một loại file/thư mục →
.claude/rules/vớipaths - Phải xảy ra mọi lần, không có ngoại lệ (lint trước commit, chặn ghi file nhạy cảm) → hook
- Quy trình nhiều bước, chỉ cần khi được gọi → skill, không nhét vào CLAUDE.md cho tốn context mọi session
Để auto memory làm phần còn lại
CLAUDE.md và .claude/rules/ đều do người viết tay. Claude Code còn một lớp thứ hai không cần gõ: auto memory, quyển sổ Claude tự ghi dựa trên những lần được sửa hoặc dặn dò trong lúc làm việc. Hai lớp này bổ sung nhau: CLAUDE.md là chỉ dẫn người viết trước, auto memory là bài học Claude tự rút ra sau.
Mỗi project có một thư mục memory riêng với file MEMORY.md đóng vai trò index, cộng các file chủ đề con như debugging.md. Chỉ 200 dòng đầu hoặc 25KB của MEMORY.md, tuỳ cái nào tới trước, được nạp vào đầu mỗi session; phần còn lại Claude đọc thêm khi cần bằng tool đọc file bình thường.
Không cần dặn Claude ghi nhớ thủ công. Lệnh /memory mở toàn bộ danh sách file CLAUDE.md lẫn memory để xem, sửa, hoặc bật/tắt cơ chế này bất cứ lúc nào.
Đặt persona đúng chỗ: không phải trong CLAUDE.md
Dòng "bạn là kỹ sư senior" trong CLAUDE.md không đổi được gì. Nội dung CLAUDE.md nạp như tin nhắn người dùng gửi sau system prompt, không phải một phần system prompt, nên khung "đóng vai chuyên gia X" ở đó không mang trọng lượng như khi đặt đúng chỗ.
Chỗ đúng là system prompt của một subagent riêng. Docs định nghĩa ví dụ subagent security-reviewer mở đầu đúng bằng persona: "You are a senior security engineer. Review code for: injection vulnerabilities...". Cùng một câu, đặt trong CLAUDE.md thì vô nghĩa, đặt trong định nghĩa subagent thì là cách chính thức để giới hạn vai trò và bộ tool của agent con.
Checklist trước khi commit CLAUDE.md
- Mỗi dòng đã tự hỏi: xoá dòng này có khiến Claude mắc lỗi không, không thì cắt
- Không dòng nào mô tả thứ Claude tự đọc code cũng biết
- Quy tắc code style viết cụ thể đủ để verify (số dấu cách, tên thư mục), không viết bằng tính từ
- File dưới 200 dòng; vượt thì tách sang
.claude/rules/theo path thay vì import lồng sâu - Thứ bắt buộc xảy ra mọi lần đã chuyển sang hook, không còn nằm ở dạng "nhắc" trong CLAUDE.md
- Không còn dòng persona kiểu "bạn là chuyên gia X" nằm lẫn trong CLAUDE.md
CLAUDE.md ngắn không tự nhiên mà có
Danh sách trên chỉ là điểm bắt đầu. Cách biết một CLAUDE.md có tác dụng thật là thử cắt bớt rồi quan sát: Claude có bắt đầu mắc lại lỗi cũ không.
Số 471 xuống 61 dòng ở phần Commands là trải nghiệm của một người dùng khác, không phải số đo sẵn cho mọi repo.
Repo đang chạy bài này dùng cả ba lớp do người viết tay: một CLAUDE.md gốc import file quy ước chung của dự án, ba rule tách theo path trong .claude/rules/, và một thư mục auto memory ghi lại thiết kế và quy ước deploy riêng.
Lớp hook nằm ở cấu hình cá nhân của người viết, không commit chung với repo, đúng ranh giới docs vạch ra: CLAUDE.md và rule là chỉ dẫn chia sẻ qua git, hook enforce cứng thường gắn với máy hoặc tổ chức.
Bài viết liên quan
tất cả bài viết →- Claude Code + NotebookLM + Obsidian: dựng pipeline nghiên cứu, và nửa cầu nối không chính chủCách ghép Claude Code, NotebookLM và Obsidian thành pipeline nghiên cứu: skill nào có sẵn, vì sao NotebookLM không có API cho tài khoản cá nhân, và cái giá phải trả khi ghép ba công cụ này.đọc tiếp →
- codebase-memory-mcp: dựng knowledge graph cho codebase và những lưu ýMột binary C index cả repo thành knowledge graph để agent hỏi bằng graph query thay vì grep từng file. Cách cài, ba tool dùng nhiều nhất, và hai lỗi im lặng làm graph thiếu mà vẫn báo indexed.đọc tiếp →
- Gemini 3.6 Flash trên Antigravity CLI — ít hơn 17% output tokenGoogle thay Gemini CLI bằng Antigravity CLI, và 3.6 Flash là model đáng để mặc định trong terminal. Cách chọn model bằng agy, chạy headless, và tính tiền cho một lượt agent.đọc tiếp →
- Lộ trình 12 tháng thành AI Engineer — không cần bằng Computer ScienceSáu phase, mỗi phase ship một thứ chạy được: Python, LLM API, RAG, agent, eval và portfolio. Lộ trình cho dev muốn chuyển sang làm AI, kèm phần thị trường Việt Nam.đọc tiếp →