Tôi từng giao cho agent một task rất nhỏ: đổi copy của một form và thêm một validation message. Nó làm đúng, chạy test đúng, rồi tiện tay format lại cả file vì Prettier version trong editor của nó khác local. Diff từ 12 dòng thành 400 dòng. Không có bug production nào, nhưng review mất gấp năm lần. Teammate đang sửa cùng file cũng phải rebase thủ công.

Lỗi không nằm ở model. Lỗi nằm ở repo chưa nói rõ “được đụng gì” và “xong nghĩa là gì”. Human dev tự hiểu một số luật ngầm: đừng format file lớn nếu task copy nhỏ, đừng refactor helper shared lúc sửa UI, đừng commit .env.local, đừng chạy migration khi chưa hỏi. Agent không có luật ngầm nếu mình không viết ra.

Bài đầu của series này là phần nhàm nhưng cứu nhiều giờ nhất: setup repo để agent làm việc có biên. Không phải setup tool. Không phải cài plugin. Là những file, rule, command và convention giúp AI coding bớt phá code.

Repo phải có một file instructions rõ

Mỗi repo nên có một file instruction ở root. Tên tùy tool: AGENTS.md, CLAUDE.md, .cursor/rules, CONTRIBUTING.md, hoặc file riêng mà bạn luôn paste vào task. Tôi thích AGENTS.md vì tên nói rõ đây là luật cho agent, không phải docs chung cho người mới.

File này không cần dài. Thực tế càng dài càng dễ bị bỏ qua. Nội dung tối thiểu:

# Agent Rules

- Do not modify files outside the requested scope.
- Do not create new API endpoints if an existing endpoint can be reused.
- Do not use mock data.
- Do not commit or push unless explicitly asked.
- Run `npm test` and `npm run typecheck` before reporting done.
- Remove debug logs before finishing.
- If requirements are ambiguous, ask before editing.

Điểm quan trọng là rule phải cụ thể đến mức có thể bắt lỗi. “Write clean code” vô dụng. “Do not create barrel files” hữu dụng. “Follow best practices” vô dụng. “Use existing hooks from src/features/*/hooks/ before creating a new data fetcher” hữu dụng.

Tôi thường chia file instructions thành năm nhóm: scope, code style, API/data, validation, git. Nếu repo có frontend, thêm import order, component naming, mock-data policy. Nếu repo có infra, thêm cloud operation policy: không delete, không modify resource remote nếu chưa có explicit confirmation.

Ghi rõ file ownership cho từng task

Instruction global chỉ là nền. Mỗi task vẫn cần file ownership riêng. Khi nhiều worker chạy song song, đây là khác biệt giữa “nhanh” và “mất buổi chiều resolve conflict”.

Một task brief tốt nên nói thẳng:

File ownership:
- You may edit src/features/reports/ExportDialog.tsx
- You may edit src/features/reports/useExportReport.ts
- Do not edit shared table components
- Do not edit package.json
- Do not reformat unrelated files

Nếu agent cần đụng file ngoài danh sách, nó phải dừng và báo. Tôi không muốn agent “linh hoạt” ở điểm này. Linh hoạt trong code implementation thì tốt; linh hoạt trong ownership thì rủi ro.

Trong repo lớn, tôi còn thêm ownership theo tầng:

  • Allowed: file được sửa.
  • Read-only context: file được đọc để hiểu behavior.
  • Forbidden: file không được đụng dù agent nghĩ cần.

Ví dụ một bug UI cần đọc API type nhưng không được sửa backend:

Allowed:
- src/features/invoices/InvoiceList.tsx
- src/features/invoices/useInvoices.ts

Read-only context:
- src/api/invoices.ts
- src/types/invoice.ts

Forbidden:
- server/**
- migrations/**

Rule này nghe cứng, nhưng nó tạo thói quen tốt. Nếu task thật sự cần backend change, đó là discovery quan trọng. Agent phải báo “frontend không đủ, cần sửa contract”, không tự mở rộng scope trong im lặng.

Dọn sẵn command kiểm tra

Agent rất hay hỏi “nên chạy test gì”. Nếu repo không ghi rõ, nó sẽ đoán: npm test, pnpm test, yarn test, npm run build, hoặc tệ hơn là không chạy gì vì command fail. Đoán command là phí token và tạo lỗi giả.

Root README hoặc instructions nên có block validation:

Validation:
- Install: pnpm install
- Typecheck: pnpm typecheck
- Unit test: pnpm test
- Lint: pnpm lint
- Build: pnpm build

Nếu command đắt, ghi rõ khi nào chạy:

Fast checks:
- pnpm typecheck
- pnpm test -- --runInBand src/features/reports

Full checks before PR:
- pnpm lint
- pnpm build
- pnpm test

Tôi thích chia “fast” và “full” vì AI coding cần feedback nhanh. Nếu mỗi vòng sửa phải chạy full suite 12 phút, agent sẽ hoặc chạy quá ít, hoặc chạy quá nhiều và đốt thời gian. Fast checks giúp nó tự sửa trong vòng ngắn. Full checks là gate trước khi báo xong.

Với repo có Docker, ghi cả platform nếu production khác máy dev. Mac ARM64 build image chạy ổn local nhưng fail trên Linux amd64 không phải chuyện hiếm.

FROM --platform=linux/amd64 node:22-bookworm

Đây không phải chi tiết “infra đẹp”. Đây là cách tránh agent tạo Dockerfile dùng Alpine rồi native module fail khi deploy.

Cấm mock data bằng rule rõ

Một failure mode phổ biến: agent không tìm thấy API hook trong 20 giây, bèn tạo mock data để UI chạy. Screenshot đẹp. Build pass. Nhưng task thật là nối với dữ liệu thật, không phải dựng demo.

Tôi luôn ghi rule này trong repo app:

Never use mock data. Existing API endpoints and hooks already exist.
Search for them first. If you cannot find one, ask.

Hai câu sau quan trọng hơn câu đầu. Chỉ nói “không mock” thì agent có thể tự tạo endpoint mới. Nói “existing hooks already exist” ép nó search trước. Nói “ask if you can’t find one” tạo stop condition.

Trong codebase thật, endpoint/hook thường nằm không đều: src/api, src/services, src/features/*/queries, src/hooks, hoặc generated client. Repo instructions nên chỉ đường:

Before adding data fetching code, check:
- src/api/
- src/services/
- src/features/*/hooks/
- src/features/*/queries/

Đừng yêu cầu agent “đọc toàn repo”. Hãy cho nó map tìm kiếm.

Git rule phải tuyệt đối

Tôi không cho agent commit hoặc push trừ khi tôi dùng đúng từ commit hoặc push. Đây là rule nhỏ nhưng thay đổi toàn bộ cảm giác làm việc. “ok”, “done”, “looks good”, “ship it” không phải permission.

Trong repo team, rule này càng quan trọng. AI diff cần review như mọi diff khác. Commit sớm làm mất cơ hội tách scope. Push sớm có thể kích hoạt CI, deploy preview, hoặc notification không cần thiết.

Rule tôi dùng:

- Never run git commit unless the user explicitly says "commit".
- Never run git push unless the user explicitly says "push".
- Do not auto-push after commit.
- Show changed files and validation result when done.

Nếu team có workflow auto-commit trong worktree riêng, viết scope riêng cho nó. Đừng để rule đặc biệt chảy vào repo chính.

Bảo vệ secrets và file local

Agent không cố ý leak secret, nhưng nó có thể stage nhầm file. Cách phòng là .gitignore đúng và pre-commit check rõ.

Tối thiểu:

.env.*
!.env.example
.local/

Và trong instructions:

Before commit, check staged files for `.env.*`.
Only `.env.example` with placeholder values may be tracked.
Credentials belong in `.local/`, which is gitignored.

Tôi thích .local/ hơn để rải secret trong nhiều file dot-env vì nó gom mọi thứ private vào một chỗ. Nếu phải move credential từ vị trí cũ, để lại breadcrumb doc ngắn nói file đã chuyển đi đâu. Breadcrumb giúp người sau không tưởng file bị mất.

Đừng để formatter tạo diff giả

AI agent thường chạy formatter theo thói quen. Formatter tốt, nhưng formatter không đồng version với repo sẽ tạo diff ồn. Có hai cách xử lý.

Cách tốt nhất là repo pin formatter version trong lockfile và có command chính thức. Agent chỉ chạy command đó.

Formatting:
- Use `pnpm format --check` for validation.
- Do not run global prettier.
- Do not reformat files outside task scope.

Cách thứ hai là không cho agent format toàn repo. Với task nhỏ, chỉ để lint/test bắt lỗi. Nếu cần format file đã sửa, command phải target đúng file.

pnpm prettier --write src/features/reports/ExportDialog.tsx

Đừng để prettier --write . xuất hiện trong AI workflow trừ khi task là format toàn repo.

Checklist setup tối thiểu

Nếu hôm nay tôi nhận một repo mới và muốn dùng AI coding an toàn, tôi sẽ làm đúng sáu việc trước:

  1. Thêm hoặc đọc AGENTS.md ở root.
  2. Ghi rõ git rule: không commit, không push nếu chưa được yêu cầu.
  3. Ghi validation command thật, gồm fast và full checks.
  4. Ghi API/data policy: không mock, reuse endpoint/hook có sẵn.
  5. Ghi file naming/import convention để agent không tạo style lạ.
  6. Kiểm tra .gitignore cho .env.*, .local/, generated artifacts.

Đây không phải bureaucracy. Đây là cách biến AI coding từ một người phụ việc hay đoán thành một worker có contract. Repo càng rõ contract, prompt mỗi task càng ngắn. Agent càng ít phải đoán, diff càng dễ review.

Chốt lại: trước khi hỏi “model nào code tốt hơn”, hãy hỏi repo của mình đã đủ dễ để một worker không biết luật ngầm vẫn làm đúng chưa. Phần lớn lỗi AI coding tôi gặp không đến từ model yếu. Chúng đến từ repo thiếu biển báo.