Sau một session dài làm việc với Claude Code, bạn giải thích cho nó biết repo này dùng convention gì, project đang ở giai đoạn nào, và vài quyết định kiến trúc quan trọng. Session kết thúc. Hôm sau mở lại, nó nhớ không?

Câu trả lời phụ thuộc hoàn toàn vào việc thông tin đó có được ghi ra file hay không. Claude Code không có bộ nhớ nội tại. Không có vector database, không có embedding store, không có session state nào được giữ lại tự động giữa hai lần khởi động. Cái gọi là “memory” trong CC hoàn toàn là file system: một folder markdown, và một file index.

Bài này mổ xẻ cơ chế đó từ đầu.

Auto-memory là gì

Auto-memory trong CC là một folder chứa các file .md, mỗi file là một memory entry. Khi session bắt đầu, binary tìm đến folder đó, đọc file MEMORY.md (index), và đưa nội dung index vào lớp L4 của prompt (tức auto-context, xem bài 1).

Model thấy index. Khi cần chi tiết, nó gọi Read để đọc file cụ thể. Không phải toàn bộ memory được nhét vào prompt mỗi turn; chỉ index được nhét. Đây là design có chủ đích: tiết kiệm token, chỉ đọc sâu khi thực sự cần.

Session bắt đầu
      |
      v
Binary tìm MEMORY.md trong memory folder
      |
      v
+----------------------------------+
|  L4: Auto-context                |
|  - nội dung MEMORY.md (index)   |
|  - date/shell/os info            |
|  - recent files, IDE selection   |
+----------------------------------+
      |
      v
Model xử lý yêu cầu user
      |
      v
Model gặp câu hỏi cần chi tiết memory?
      |
      +-- Có --> gọi Read(<memory-file>.md) --> lấy đủ thông tin
      |
      +-- Không --> tiếp tục không đọc file memory cụ thể

Cách này giống “index trước, lazy-load sau” mà bạn gặp trong mọi database. Nếu model cần thông tin về một project, nó đọc đúng file project đó, không đọc toàn bộ memory folder.

Folder memory và cấu trúc file

Mặc định, memory của một project nằm ở:

~/.claude/projects/<workdir-hash>/memory/

Bạn có thể override path này qua autoMemoryDirectory trong settings.json để nhiều project chia sẻ chung một memory folder. Bài 14 đi sâu vào pattern đó.

Cấu trúc một folder memory điển hình:

memory/
├── MEMORY.md              <- index, được nạp tự động
├── user_profile.md        <- 1 entry
├── project_state.md       <- 1 entry
├── feedback_git_workflow.md  <- 1 entry
└── reference_deploy.md    <- 1 entry

Mỗi file là một entry độc lập. Index (MEMORY.md) ghi 1 dòng mỗi entry, theo format:

- [tên-file.md](tên-file.md) — Mô tả ngắn về nội dung entry này.

Ví dụ thực tế (generic hóa):

# Memory Index

- [user_profile.md](user_profile.md) — Full-stack developer, 10+ năm, stack React/Node/K8s.
- [project_state.md](project_state.md) — Project đang ở Phase 2, API refactor, chưa có test coverage.
- [feedback_git_workflow.md](feedback_git_workflow.md) — Không push thẳng main. Ticket ID bắt buộc trong commit message.
- [reference_deploy.md](reference_deploy.md) — Deploy qua wrangler. Phải unset CLOUDFLARE_API_KEY trước.

Khi model đọc index này, nó biết: có bốn thứ đáng nhớ. Nếu bạn hỏi về deployment, model gọi Read(reference_deploy.md) để lấy đủ chi tiết. Nếu bạn hỏi về commit format, model đọc feedback_git_workflow.md.

Frontmatter mỗi memory entry

Mỗi file memory có frontmatter YAML ở đầu. Các trường bắt buộc:

---
name: tên ngắn gọn
description: mô tả 1-2 dòng về nội dung
type: user | feedback | project | reference
---

Trường type quan trọng nhất vì nó phân loại memory và ảnh hưởng đến cách model suy diễn về độ tươi và cách dùng thông tin.

Bốn type memory

user

Profile của người dùng. Thông tin ổn định, ít thay đổi: skill set, ngôn ngữ ưu tiên, stack làm quen, thiết lập máy.

Ví dụ:

---
name: developer profile
description: Background và tech stack của developer, dùng để calibrate level giải thích.
type: user
---

Body có thể là: ngôn ngữ nào thoải mái nhất, quen với framework nào, đang học gì, OS đang dùng.

Model dùng loại này để calibrate tone và độ sâu giải thích. Nếu profile ghi “senior full-stack, quen với K8s”, model không giải thích lại pod là gì mỗi lần.

feedback

Correction và preference từ user. Đây là loại memory sinh ra từ “tôi đã nói điều này và CC làm sai, tôi đã correct nó”. Hoặc từ preference rõ ràng mà user express trong session.

Ví dụ:

---
name: git commit convention
description: Commit format bắt buộc: prefix(ticket-id), không Co-Authored-By, không emoji.
type: feedback
---

Body ghi: quy tắc cụ thể, lý do, ví dụ đúng/sai.

Loại này quan trọng nhất để tránh lặp lại sai lầm. Nếu bạn đã correct CC về một behavior trong session hôm qua, feedback memory là thứ ngăn nó tái phạm hôm nay.

project

State hiện tại của project. Không phải spec, không phải README, mà là “hiện tại project đang ở đâu”: phase, milestone đã xong, milestone tiếp theo, quyết định đã lock.

Ví dụ:

---
name: project api-service state
description: Trạng thái Phase 2 refactor: endpoint auth đã xong, endpoint billing còn 3 file cần migrate.
type: project
---

Body có thể là: danh sách file đã migrate, danh sách file chưa làm, quyết định kiến trúc đã lock (ví dụ “không dùng ORM, raw SQL cho toàn bộ billing module”), blocker đang gặp.

Loại này có shelf-life ngắn nhất. Project state thay đổi mỗi ngày. Xem phần “Stale memory” bên dưới.

reference

Pointer tới resource bên ngoài hoặc tóm tắt kỹ thuật ổn định. Không phải state, không phải preference, mà là “đây là cách làm đúng cho X, hãy nhớ điều này”.

Ví dụ:

---
name: wrangler deploy gotcha
description: Cloudflare API key env var conflict với wrangler auth. Fix: unset trước khi deploy.
type: reference
---

Body là procedure cụ thể, lý do kỹ thuật, và ví dụ command.

Loại này ổn định nhất. Một gotcha kỹ thuật đã documented không thay đổi trừ khi tool thay đổi behavior. Khi đó bạn cần update thủ công.

Bảng tóm tắt bốn type

TypeNội dung điển hìnhTần suất thay đổiKhi nào save
userProfile, stack, thiết bịHiếmLần đầu setup, hoặc khi stack thay đổi lớn
feedbackCorrection, preferenceTrung bìnhMỗi khi CC làm sai và bạn correct nó
projectPhase, milestone, quyết định lockThườngSau mỗi milestone, sau quyết định kiến trúc
referenceProcedure, gotcha, pointerHiếmKhi phát hiện gotcha mới hoặc pattern mới

Khi nào save memory

Nguyên tắc chung: save khi thông tin đó sẽ còn hữu ích trong session tương lai và không thể suy ra từ codebase.

Nên save:

  • User feedback rõ ràng: “từ giờ đừng dùng X”, “luôn làm Y theo cách Z”.
  • Quyết định kiến trúc đã lock: “billing module dùng raw SQL, không ORM”.
  • Gotcha kỹ thuật đã confirm: deploy flow cần unset env var, test runner cần flag cụ thể.
  • Project milestone: phase nào đã xong, file nào đã migrate, blocker nào đang chờ.

Không nên save:

  • Task detail ephemeral: “hôm nay fix bug ở line 42 file auth.ts”. Cái này không còn giá trị sau khi bug được fix.
  • Code pattern: model nên đọc từ codebase, không nhớ trong memory. Nếu pattern quan trọng, ghi vào CLAUDE.md hoặc rule file.
  • Output của một command cụ thể: log, stack trace, test result. Những cái này thay đổi liên tục.
  • Thông tin đã có trong CLAUDE.md hoặc rules: duplicate memory làm tăng token và gây conflict nếu hai chỗ mâu thuẫn.

Stale memory và cách handle

Memory có thể outdated. Project state từ 3 tuần trước có thể sai hoàn toàn nếu bạn đã refactor xong Phase 2. Reference về một CLI tool có thể sai nếu tool đã ra version mới.

Nguyên tắc: verify trước khi act on memory.

Model không nên dùng project state memory làm ground truth tuyệt đối. Nếu memory ghi “file A chưa migrate” nhưng bạn hỏi về file A, model nên check thực tế (đọc file, grep) trước khi kết luận.

Dấu hiệu memory có thể stale:

  • type: project và ngày ghi chú đã lâu (nếu bạn ghi timestamp trong body).
  • Nội dung mô tả file cụ thể mà file đó có thể đã thay đổi.
  • Quyết định kiến trúc mà bạn nhớ là mình đã đổi nhưng chưa update memory.

Cách handle:

  1. Thêm timestamp trong body của project memory khi save.
  2. Định kỳ review và update sau mỗi milestone lớn.
  3. Khi nhận thấy memory conflict với thực tế, update ngay thay vì để đó.

Bạn có thể bảo CC update memory trong session:

"Update memory: Phase 2 refactor đã xong. Tất cả billing endpoints đã migrate sang raw SQL."

CC sẽ gọi Edit để cập nhật file memory tương ứng.

MEMORY.md là index, không phải nội dung

Điểm hay nhầm lẫn: nhiều người nghĩ MEMORY.md chứa toàn bộ memory. Không phải. MEMORY.md chỉ là index.

Lợi ích của thiết kế này:

  • Index ngắn, token cost thấp. Nếu bạn có 20 entry memory, index chỉ tốn ~500 token cho 20 dòng. Nếu nhét toàn bộ 20 file vào prompt thì có thể tốn 5.000-10.000 token.
  • Model đọc sâu khi cần. Không bao giờ đọc 20 file mà chỉ cần 1.
  • Tổ chức tốt hơn. Index buộc mỗi entry phải có tên và mô tả rõ ràng, thay vì dump mọi thứ vào một file dài.

Khi viết MEMORY.md thủ công hoặc update, giữ format nghiêm:

# Memory Index

- [tên.md](tên.md) — Mô tả ngắn, đủ để model biết có nên đọc tiếp không.

Mô tả trong index phải đủ informative để model ra quyết định đọc hay không. Mô tả quá mơ hồ (“project stuff”) vô dụng; mô tả quá dài thì mất ý nghĩa “index”.

Cách model dùng memory trong thực tế

Một session điển hình với memory hoạt động tốt:

  1. Session bắt đầu. Binary đọc MEMORY.md, đưa index vào L4.
  2. Model thấy index trong prompt. Ví dụ thấy entry: “project_state.md: API refactor Phase 2, billing còn 3 file chưa xong”.
  3. Bạn hỏi: “Còn file nào cần migrate không?”
  4. Model gọi Read(project_state.md) để lấy chi tiết danh sách file.
  5. Model trả lời dựa trên chi tiết đó, đồng thời có thể verify bằng Glob hoặc Grep thực tế trong codebase.

Lưu ý bước 5: model tốt không chỉ tin memory mù quáng. Nó cross-check với codebase thực. Memory là hint, không phải source of truth tuyệt đối.

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

  • Auto-memory là folder chứa file .md. CC nạp MEMORY.md (index) vào L4 của prompt mỗi session, không nhét toàn bộ memory vào prompt.
  • Mỗi entry có frontmatter với trường type gồm 4 loại: user (profile), feedback (correction/preference), project (state hiện tại), reference (procedure/gotcha).
  • Save khi thông tin không thể suy ra từ codebase và còn hữu ích trong session tương lai. Không save task detail ephemeral hoặc code pattern.
  • Stale memory là risk thực sự, nhất là type: project. Verify trước khi act on memory.
  • MEMORY.md là index, không phải nội dung. Giữ mô tả đủ informative để model quyết định có đọc file cụ thể không.

Bài 13 sẽ đi vào pattern phức tạp hơn: dùng git submodule để memory folder được version-controlled độc lập và chia sẻ giữa nhiều máy. Đây là cách giữ memory consistent khi bạn làm việc trên cả laptop lẫn homelab server mà không cần sync thủ công.

Bài 14 tiếp tục với autoMemoryDirectory: cách nhiều project cùng trỏ vào một memory folder, phân chia namespace, và tránh leak context giữa các project khác nhau.


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