Bản đồ công trìnhTờ này lắp: PRD & spec, Giao diện app, API
Bài nàyĐã họcChưa tớiĐã thay thế
Bạn sẽ học được gì
Tiêu chí nghiệm thu
Vì sao tài liệu quan trọng hơn khi có AI?
Hãy so sánh hai cách giao việc cho một thợ bánh mới vào làm. Cách thứ nhất: “Làm cho chị cái bánh sinh nhật nhé.” Cách thứ hai: một phiếu đặt bánh ghi rõ size 20cm, vị chanh dây, chữ “Mừng sinh nhật Mẹ Ánh” (có dấu), nến số 6, giao trước 17:00, không dùng hạt vì khách dị ứng. Thợ giỏi đến mấy, cách thứ nhất cũng đòi hỏi họ đoán hàng chục điều — và mỗi lần đoán sai, bạn chỉ phát hiện ra khi bánh đã xong.
Coding agent là “thợ mới vào làm” mỗi khi bạn mở phiên mới: nó giỏi kỹ thuật, nhưng không nhớ gì về cuộc trò chuyện hôm qua, không biết anh Tuấn sắp bánh theo chuyến xe, không biết Hà hay bấm nút hai lần khi mạng chậm. Một prompt mơ hồ buộc mô hình đoán hàng loạt yêu cầu ngầm, và những lần đoán sai thường lộ ra rất muộn. Spec là phiếu đặt bánh của phần mềm.
Thêm một lý do: ngữ cảnh là tài nguyên quý nhất của agent (xem bài context engineering02-05). Một spec cô đọng 2–4 trang chứa đúng những gì cần cho một tính năng tốt hơn rất nhiều so với việc để agent đọc lại 50 tin nhắn trao đổi hoặc tự khám phá hàng chục file.
PRD tổng thể và spec tính năng
| PRD (Product Requirements Document) | Spec tính năng | |
|---|---|---|
| Phạm vi | Toàn bộ sản phẩm hoặc một giai đoạn (ví dụ MVP Mộc OMS) | Một tính năng (ví dụ “Tạo đơn hàng”) |
| Câu hỏi chính | Làm gì, cho ai, vì sao, thành công là gì, không làm gì | Chính xác hành vi nào, quy tắc nào, dữ liệu nào, kiểm chứng ra sao |
| Độ dài gợi ý | 1–3 trang | 2–5 trang |
| Người đọc | Chủ doanh nghiệp, team, đối tác, agent (làm bối cảnh) | Agent thực thi, người review, người kiểm thử |
| Tần suất thay đổi | Ít, theo giai đoạn | Thường xuyên trong lúc làm tính năng |
| Vị trí trong repo | docs/PRD.md |
docs/specs/tao-don-hang.md |
| Có nói về công nghệ? | Chỉ ràng buộc lớn (stack đã chọn, ngân sách) | Có thể chỉ ra file, interface, bảng dữ liệu liên quan |
Quan hệ giữa các tài liệu bạn đã tạo trong module này:
Vuốt ngang để xem hết sơ đồ
Đọc sơ đồ dạng chữ
- Problem statement (bài 03-01) → docs/PRD.md
- Nghiên cứu, quy tắc nghiệp vụ (bài 03-02) → docs/PRD.md
- MoSCoW, non-goals (bài 03-04) → docs/PRD.md
- docs/PRD.md → docs/specs/tao-don-hang.md
- docs/PRD.md → docs/specs/man-hinh-bep.md
- User story + Given–When–Then (bài 03-05) → docs/specs/tao-don-hang.md
- User story + Given–When–Then (bài 03-05) → docs/specs/man-hinh-bep.md
- docs/specs/tao-don-hang.md → Kế hoạch kỹ thuật (plan)
- Kế hoạch kỹ thuật (plan) → Danh sách task
- Danh sách task → Code + test
- Code + test → docs/specs/tao-don-hang.md (phát hiện điều chưa rõ)
Mũi tên nét đứt cuối cùng là chỗ nhiều người bỏ qua: khi làm, bạn sẽ phát hiện điều spec chưa nói. Đường đúng là quay lại cập nhật spec, không phải “sửa nhanh trong code rồi tính sau”.
Mẫu PRD 1–3 trang
Một PRD cho dự án nhỏ không cần 40 trang. Nó cần đủ để một người mới (hoặc một agent) đọc trong 10 phút là hiểu sản phẩm này tồn tại để làm gì và ranh giới nằm ở đâu. Đây là PRD của Mộc OMS, rút gọn từ các bài trước:
# PRD — Mộc OMS (giai đoạn 1: MVP)
- Phiên bản: 1.2 | Cập nhật: 2026-01-19 | Chủ sở hữu: Nguyễn Thị Lan (owner)
- Trạng thái: Đã duyệt
## 1. Tóm tắt
Mộc OMS là hệ thống quản lý đơn nội bộ cho chuỗi 3 tiệm Bánh Mộc (Phú Nhuận, Thảo Điền, Gò Vấp)
và bếp trung tâm. Mục tiêu giai đoạn 1: mọi đơn đặt trước được nhập MỘT LẦN tại nguồn,
bếp và chi nhánh đọc từ cùng một chỗ, tiền cọc gắn với đơn.
## 2. Vấn đề
Xem problem statement đầy đủ ở phần Phụ lục A. Tóm tắt: đơn bị chép lại 2–3 lần qua Sheet, sổ tay,
ảnh chụp Zalo; ước tính thiệt hại trực tiếp 13–24 triệu đồng/tháng (giả định, đang đo baseline).
## 3. Mục tiêu và chỉ số thành công (sau 3 tháng dùng ở cả 3 tiệm)
- Tỷ lệ bánh kem làm lại do sai thông tin giảm ≥ 50% so với baseline.
- Thời gian đối soát cuối ngày ≤ 30 phút/tiệm.
- ≥ 95% đơn đặt trước được tạo trong Mộc OMS (không còn đơn chỉ nằm trên Sheet/Zalo).
- Chỉ số định hướng và kế hoạch đo: docs/metrics.md.
## 4. Người dùng
- staff: Hà (bán hàng online), nhân viên quầy — tạo, sửa đơn; ghi cọc.
- manager: quản lý chi nhánh, anh Tuấn (bếp trung tâm) — như staff + hủy đơn đang làm, sửa giá, tạo đơn gấp.
- owner: chị Lan — xem toàn chuỗi, quản lý nhân viên và danh mục.
- Persona chi tiết: docs/research/personas.md.
## 5. Phạm vi MVP
| Mã | Tính năng | Story | Spec |
|---|---|---|---|
| F1 | Tạo đơn hàng (kèm tìm/tạo khách, ghi cọc khi tạo) | US-01, US-02, US-03 | specs/tao-don-hang.md |
| F2 | Sửa, hủy đơn; chuyển trạng thái | US-03, US-05 | specs/cap-nhat-don.md |
| F3 | Màn hình bếp theo chuyến xe | US-04 | specs/man-hinh-bep.md |
| F4 | Khách hàng và lịch sử mua | US-02 | specs/khach-hang.md |
| F5 | Danh mục sản phẩm, bánh kem tùy chỉnh | — | specs/san-pham.md |
| F6 | Đăng nhập theo vai trò, phân quyền theo chi nhánh | — | specs/phan-quyen.md |
| F7 | Dashboard đơn hôm nay | US-06 | specs/dashboard.md |
## 6. Ngoài phạm vi (non-goals)
Website cho khách tự đặt; chatbot/trợ lý AI; thay máy tính tiền, hóa đơn điện tử; tự xác nhận chuyển khoản;
thông báo tự động cho khách; kho nguyên liệu; tích điểm, khuyến mãi; app di động cài đặt;
tự tính phí giao hàng; multi-tenant. Lý do và thời điểm xem lại: Phụ lục B.
## 7. Ràng buộc và giả định
- Stack: Next.js + TypeScript + Supabase (PostgreSQL, Auth) + Drizzle + Zod; host Vercel.
- Tiền lưu số nguyên VNĐ; thời gian lưu timestamptz, hiển thị Asia/Ho_Chi_Minh.
- Dữ liệu cá nhân khách (tên, SĐT, địa chỉ, sinh nhật) chỉ dùng cho mục đích phục vụ đơn hàng;
tuân thủ quy định bảo vệ dữ liệu cá nhân hiện hành (cần tư vấn pháp lý khi mở rộng sử dụng).
- Thiết bị: laptop ở quầy, máy tính bảng ở bếp, điện thoại của người giao. Chỉ tiếng Việt.
- Nguồn lực: chị Lan ~8–10 giờ/tuần cùng coding agent; review bảo mật thuê ngoài trước khi ra mắt.
## 8. Rủi ro chính
- Nhân viên quay lại Sheet nếu tạo đơn chậm hơn cách cũ -> mục tiêu tạo đơn ≤ 90 giây.
- Sai phân quyền làm lộ dữ liệu khách giữa chi nhánh -> test phân quyền bắt buộc.
- Ngày lễ quá tải -> chạy thử trước mùa cao điểm ít nhất 2 tuần.
## 9. Mốc
- Lát 1 chạy thử ở Phú Nhuận + bếp trung tâm: 2 tuần.
- Lát 2, mở cho 3 tiệm: sau khi lát 1 đạt tiêu chí go/no-go.
## 10. Câu hỏi mở
- Bánh nhận trước 08:00 đi chuyến nào? (chờ anh Tuấn xác nhận)
## 11. Lịch sử thay đổi
- 1.2 (2026-01-19): thêm quy tắc chuyến xe QT-07 sau shadowing bếp.
- 1.1: thêm F4 lịch sử mua vào lát 2 theo kết quả phỏng vấn khách.
Mẫu spec tính năng
Spec tính năng tốt có bốn đặc điểm, rút ra từ kinh nghiệm làm việc với coding agent:
- Tự chứa (self-contained): một agent ở phiên mới đọc spec (cùng các file nó trỏ tới) là đủ để làm, không cần lịch sử trò chuyện.
- Chỉ ra file và interface liên quan: bảng dữ liệu nào, thư mục nào, hàm nào đã có để tái sử dụng — giúp agent khỏi “khám phá vô hạn” codebase.
- Nói rõ ngoài phạm vi: hàng rào chống “tiện tay làm thêm”.
- Kết thúc bằng cách kiểm chứng end-to-end: lệnh test nào, kịch bản thủ công nào, bằng chứng nào cần nộp.
Khung mẫu bạn có thể lưu thành docs/specs/_template.md:
# Spec: [Tên tính năng]
- Mã: SPEC-xxx | Trạng thái: Nháp / Đang review / Đã duyệt / Đã triển khai | Phiên bản: x.y
- Cập nhật: YYYY-MM-DD | Người phụ trách: ...
- Liên quan: PRD mục ..., user story ..., spec khác ...
## 1. Mục tiêu
Một đoạn: tính năng này giải phần nào của vấn đề trong PRD; thành công trông như thế nào.
## 2. Người dùng và quyền
Ai dùng, ở thiết bị nào; mỗi vai trò được làm gì, không được làm gì.
## 3. Trong phạm vi
Danh sách hành vi cụ thể.
## 4. Ngoài phạm vi
Những gì KHÔNG làm trong spec này (kể cả những thứ "tiện thể").
## 5. Luồng chính
Các bước người dùng đi qua, từ điểm vào đến kết quả.
## 6. Quy tắc nghiệp vụ
Đánh mã (QT-xx), mỗi quy tắc kiểm tra được.
## 7. Dữ liệu
Bảng/cột đọc và ghi; giá trị mặc định; ràng buộc; những gì phải ghi lịch sử.
## 8. Giao diện
Màn hình, đường dẫn, các trường và thứ tự; trạng thái rỗng/đang tải/lỗi/thành công.
## 9. Hợp đồng phía máy chủ
Tên hàm/endpoint, đầu vào, đầu ra, mã lỗi.
## 10. Lỗi và thông báo
Bảng: tình huống -> thông báo hiển thị (tiếng Việt, tử tế, chỉ cách sửa).
## 11. Tiêu chí chấp nhận
Given–When–Then hoặc trỏ tới file story.
## 12. Kiểm chứng end-to-end
Lệnh chạy test; kịch bản thủ công; bằng chứng cần nộp (output test, ảnh chụp).
## 13. Câu hỏi mở
Điều chưa quyết định; đánh dấu [CẦN XÁC NHẬN].
## 14. Lịch sử thay đổi
Spec hoàn chỉnh: “Tạo đơn hàng”
Đây là spec mà chị Lan dùng để giao cho agent. Bạn sẽ thấy nó được thực thi từng bước ở bài case study 06-1006-10. Hãy đọc kỹ cách spec nói rất rõ “cái gì” nhưng để ngỏ “làm thế nào” ở những chỗ thuộc về kế hoạch kỹ thuật (ví dụ cách sinh mã đơn an toàn khi tạo đồng thời).
# Spec: Tạo đơn hàng
- Mã: SPEC-001 | Trạng thái: Đã duyệt | Phiên bản: 1.1
- Cập nhật: 2026-01-26 | Người phụ trách: Nguyễn Thị Lan
- Liên quan: docs/PRD.md (F1); docs/specs/user-stories.md (US-01, US-02, US-03 phần ghi cọc khi tạo)
## 1. Mục tiêu
Cho phép nhân viên ghi một đơn (đặc biệt đơn bánh kem tùy chỉnh) MỘT LẦN, đầy đủ thông tin bếp cần,
trong ≤ 90 giây với khách đã có. Thay thế việc gõ Google Sheet và chụp màn hình gửi bếp.
## 2. Người dùng và quyền
- staff (Hà, nhân viên quầy): tạo đơn cho chi nhánh của mình; KHÔNG sửa đơn giá; KHÔNG tạo đơn gấp.
- manager: như staff + sửa đơn giá + tạo đơn gấp (bắt buộc lý do).
- owner: như manager, cho mọi chi nhánh.
- Thiết bị chính: laptop ở quầy (màn hình ≥ 1280px); phải dùng được trên máy tính bảng.
## 3. Trong phạm vi
- Tìm khách theo số điện thoại; tạo khách mới (họ tên, SĐT bắt buộc; địa chỉ, sinh nhật tùy chọn).
- Thêm 1..n dòng sản phẩm; với sản phẩm is_custom_cake: chọn vị, chữ trên bánh (hoặc "không chữ"), ghi chú cho bếp.
- Chọn kênh (facebook | zalo | phone | walk_in | website), chi nhánh nhận, nhận tại tiệm hoặc giao hàng,
giờ nhận/giao, địa chỉ giao (nếu giao), phí giao (nhập tay), ghi chú đơn.
- Ghi nhận tiền cọc ngay khi tạo (tùy chọn): phương thức, số tiền, mã giao dịch.
- Sinh mã đơn; lưu đơn ở trạng thái moi; ghi dòng đầu tiên vào lịch sử trạng thái.
- Sau khi tạo: hiển thị mã đơn và nút "Sao chép tóm tắt" để gửi khách xác nhận.
## 4. Ngoài phạm vi
- Sửa, hủy, chuyển trạng thái đơn (SPEC-002).
- Hiển thị ở màn hình bếp (SPEC-003) — spec này chỉ cần đảm bảo dữ liệu đủ cho bếp.
- Gửi tin nhắn/email/Zalo tự động cho khách; tạo mã VietQR; xác nhận chuyển khoản tự động.
- Tự tính phí giao hàng; mã giảm giá; tích điểm; xuất hóa đơn.
- Trang quản trị danh mục sản phẩm (dùng dữ liệu seed).
- KHÔNG tự thêm tính năng ngoài danh sách trên; đề xuất ghi vào mục 13.
## 5. Luồng chính
1. Nhân viên mở /orders/new.
2. Nhập SĐT -> hệ thống chuẩn hóa và tìm khách. Thấy: hiện tên + 3 đơn gần nhất. Không thấy: form khách mới, SĐT điền sẵn.
3. Thêm sản phẩm. Nếu là bánh kem tùy chỉnh: hiện thêm trường vị, chữ trên bánh, ghi chú bếp.
4. Chọn kênh, chi nhánh (mặc định chi nhánh của nhân viên), nhận/giao, giờ nhận.
5. (Tùy chọn) ghi nhận cọc.
6. Xem tóm tắt: tạm tính, phí giao, tổng, đã cọc, còn lại. Bấm "Tạo đơn".
7. Thành công: hiện mã đơn, tóm tắt; nút "Sao chép tóm tắt", "Tạo đơn mới".
## 6. Quy tắc nghiệp vụ
- QT-01: bánh kem tùy chỉnh bắt buộc vị; size xác định bởi sản phẩm (ví dụ "Bánh kem tùy chỉnh 20cm");
chữ trên bánh tối đa 40 ký tự, giữ nguyên dấu; không chấp nhận emoji (bếp không viết được);
nếu khách không muốn chữ: chọn "Không chữ" (cake_message = null).
- QT-02: bánh kem tùy chỉnh: delivery_at ≥ thời điểm tạo + 24 giờ. staff bị chặn.
manager/owner được tạo "đơn gấp" khi nhập lý do; lý do lưu đầu trường note dạng "[ĐƠN GẤP] <lý do>".
- QT-03: delivery_at trong khung 07:00–21:00 giờ Việt Nam, không ở quá khứ.
- QT-05: SĐT chuẩn hóa về 10 chữ số bắt đầu bằng 0 trước khi tìm/lưu; customers.phone là UNIQUE.
- QT-06: unit_price lấy từ products.price tại thời điểm tạo; chỉ manager/owner sửa được.
- QT-09: mã đơn dạng BM-YYMMDD-XXX theo NGÀY TẠO giờ Việt Nam; XXX tăng dần từ 001 trong ngày, toàn chuỗi;
không bao giờ trùng, kể cả khi nhiều người tạo cùng lúc.
- QT-10: tiền là số nguyên VNĐ. subtotal = tổng(quantity × unit_price); total = subtotal + shipping_fee.
- QT-11: cọc khi tạo: 0 < số tiền ≤ total. Nếu số tiền = total -> payment_status = paid; nếu > 0 -> deposit;
không cọc -> unpaid. Ghi một dòng vào payments.
- QT-12: delivery_type = delivery thì bắt buộc delivery_address.
- QT-13: bấm "Tạo đơn" nhiều lần (hoặc gửi lại do mạng) chỉ tạo đúng một đơn.
## 7. Dữ liệu
- Đọc: customers (tìm theo phone), products (is_active = true), staff (vai trò, branch_id của người dùng).
- Ghi trong MỘT giao dịch (tất cả hoặc không gì cả):
- customers: tạo mới nếu chưa có.
- orders: code, customer_id, branch_id, channel, status = 'moi', delivery_type, delivery_at, delivery_address,
subtotal, shipping_fee, total, deposit (tổng đã cọc), payment_status, note, created_by = staff.id.
- order_items: product_id, quantity, unit_price, cake_message,
options JSONB ví dụ {"flavor": "chanh_day", "kitchen_note": "ít ngọt"}.
- payments (nếu có cọc): method (cash | bank_transfer | vietqr), amount, paid_at, reference.
- order_status_history: from_status = null, to_status = 'moi', changed_by, changed_at.
- Không thay đổi schema trong spec này. Nếu thấy cần, dừng lại và ghi vào mục 13.
## 8. Giao diện
- Đường dẫn: /orders/new. Một trang, chia 4 khối theo thứ tự: Khách hàng -> Sản phẩm -> Nhận hàng -> Thanh toán,
bên phải là khối Tóm tắt luôn hiển thị.
- Thứ tự trường bánh kem theo mẫu tin nhắn Hà đang dùng: Sản phẩm (size) -> Vị -> Chữ trên bánh -> Ghi chú bếp.
- Bộ đếm ký tự cho chữ trên bánh (x/40).
- Trạng thái: đang tìm khách; không tìm thấy; đang lưu (nút bị vô hiệu hóa, chữ "Đang tạo đơn..."); lỗi; thành công.
- Lỗi hiện ngay dưới trường liên quan; không xóa dữ liệu đã nhập khi lỗi.
- Dùng component có sẵn trong src/components/ui (shadcn/ui); không thêm thư viện giao diện mới.
## 9. Hợp đồng phía máy chủ
- Server action createOrder(input) trong src/features/orders/actions.ts.
- Schema Zod createOrderSchema trong src/features/orders/schema.ts, dùng chung cho client và server.
- Quy tắc thuần (QT-02, QT-03, QT-10, QT-11) đặt trong src/features/orders/rules.ts, có unit test.
- normalizePhone đã có ở src/lib/phone.ts — tái sử dụng, không viết lại.
- Đầu vào: thông tin khách (customerId hoặc khách mới), danh sách dòng, kênh, chi nhánh, nhận/giao, giờ nhận,
phí giao, ghi chú, cọc (tùy chọn), rushReason (tùy chọn), idempotencyKey (chuỗi do client sinh mỗi lần mở form).
- Đầu ra thành công: { ok: true, order: { id, code } }.
- Đầu ra lỗi: { ok: false, code, fieldErrors? } với code thuộc:
VALIDATION_ERROR | LEAD_TIME_TOO_SHORT | OUTSIDE_OPENING_HOURS | DEPOSIT_EXCEEDS_TOTAL | FORBIDDEN | UNKNOWN.
- Mọi kiểm tra quyền và quy tắc thực hiện ở máy chủ; kiểm tra phía client chỉ để phản hồi nhanh.
## 10. Lỗi và thông báo
| Tình huống | Thông báo |
|---|---|
| Thiếu vị bánh | "Vui lòng chọn vị bánh." |
| Chữ > 40 ký tự | "Chữ trên bánh tối đa 40 ký tự (hiện có N)." |
| Chữ có emoji | "Chữ trên bánh không dùng được biểu tượng cảm xúc." |
| Đặt trước < 24 giờ (staff) | "Bánh kem tùy chỉnh cần đặt trước ít nhất 24 giờ. Liên hệ quản lý để tạo đơn gấp." |
| Ngoài giờ mở cửa | "Giờ nhận cần trong khoảng 07:00–21:00." |
| SĐT sai | "Số điện thoại cần 10 chữ số, bắt đầu bằng 0." |
| Cọc > tổng | "Tiền cọc không được lớn hơn tổng đơn." |
| Mất kết nối | "Chưa tạo được đơn do mất kết nối. Thông tin vẫn được giữ, vui lòng thử lại." |
## 11. Tiêu chí chấp nhận
Toàn bộ kịch bản US-01, US-02 trong docs/specs/user-stories.md, cộng thêm:
- Given hai nhân viên bấm "Tạo đơn" cùng lúc, Then hai đơn có mã khác nhau.
- Given đơn tạo lúc 23:59 ngày 12/10 và 00:01 ngày 13/10 (giờ VN), Then mã lần lượt BM-261012-xxx và BM-261013-001 (nếu là đơn đầu ngày).
- Given staff chi nhánh Gò Vấp, When gửi createOrder với branch_id của Phú Nhuận, Then nhận FORBIDDEN.
## 12. Kiểm chứng end-to-end
- Chạy: npm run lint, npm run typecheck, npm test (unit), npm run test:e2e (Playwright).
- Unit test bắt buộc: QT-01, QT-02, QT-03, QT-05, QT-10, QT-11, QT-13 (mỗi test bắt đầu bằng mã quy tắc).
- E2E bắt buộc: luồng chính US-01; khách mới US-02; staff bị chặn đơn gấp.
- Kiểm thử thủ công trên máy tính bảng: tạo 1 đơn bánh kem có chữ "Mừng sinh nhật Mẹ Ánh", chụp màn hình kết quả.
- Bằng chứng nộp kèm PR: output các lệnh trên, 2 ảnh chụp (form đầy đủ, màn hình thành công), danh sách file đã đổi.
## 13. Câu hỏi mở
- [CẦN XÁC NHẬN] Có nên tách lý do đơn gấp thành cột riêng thay vì tiền tố trong note? (ảnh hưởng báo cáo sau này)
- [CẦN XÁC NHẬN] Khách chỉ có Zalo, không cung cấp SĐT: MVP bắt buộc SĐT; xem lại ở giai đoạn trợ lý AI.
## 14. Lịch sử thay đổi
- 1.1 (2026-01-26): thêm QT-13 (chống tạo trùng) và cấm emoji trong chữ trên bánh sau buổi đọc spec với Hà.
- 1.0: bản đầu tiên, viết sau phiên agent phỏng vấn.
Một vài chi tiết đáng chú ý trong spec này:
- Mục 9 chỉ ra file có sẵn (
normalizePhone) và dặn tái sử dụng. Thiếu dòng này, agent rất có thể viết thêm một hàm chuẩn hóa thứ hai hơi khác — và bạn có hai “sự thật” về số điện thoại. - QT-09 nói “không bao giờ trùng, kể cả khi tạo cùng lúc” nhưng không nói cách làm. Đó là việc của bước lập kế hoạch kỹ thuật, nơi agent đề xuất phương án (ví dụ dựa vào ràng buộc
UNIQUEvà cơ chế đếm trong database) để bạn duyệt. - “Không thay đổi schema trong spec này” là một hàng rào quan trọng: thay đổi mô hình dữ liệu cần được cân nhắc riêng (xem bài mô hình hóa dữ liệu04-02).
- Mục 12 yêu cầu bằng chứng, không chỉ “chạy test”. Agent phải nộp output và ảnh chụp, giống như thợ bánh chụp ảnh bánh trước khi giao.
Để agent phỏng vấn bạn rồi viết SPEC.md
Viết spec từ trang trắng rất khó với người mới. Một kỹ thuật hiệu quả mà tài liệu thực hành của nhiều nhà cung cấp coding agent khuyến nghị cho tính năng lớn: đảo vai — để agent phỏng vấn bạn. Agent giỏi đặt những câu hỏi về điểm khó mà bạn chưa nghĩ tới (đồng thời, phân quyền, dữ liệu cũ, múi giờ), còn bạn giỏi trả lời về nghiệp vụ. Kết quả là spec vừa đúng nghiệp vụ, vừa đủ chi tiết kỹ thuật.
Quy trình gồm hai phiên tách biệt:
Vuốt ngang để xem hết sơ đồ
Đọc sơ đồ dạng chữ
- Chị Lan → Phiên 1: agent phỏng vấn: Bối cảnh + yêu cầu phỏng vấn (chế độ chỉ đọc)
- Phiên 1: agent phỏng vấn → Phiên 1: agent phỏng vấn: Đọc PRD, user stories, schema
- Lặp: Từng nhóm 3–5 câu hỏi
- Phiên 1: agent phỏng vấn → Chị Lan: Câu hỏi (kể cả điểm khó)
- Chị Lan → Phiên 1: agent phỏng vấn: Trả lời / “chưa biết
- Phiên 1: agent phỏng vấn → docs/specs/tao-don-hang.md: Viết spec theo mẫu, đánh dấu [CẦN XÁC NHẬN]
- Chị Lan → docs/specs/tao-don-hang.md: Đọc, sửa, đọc cùng Hà, chuyển “Đã duyệt
- Chị Lan → Phiên 2 (sạch): agent thực thi: Đọc spec, lập kế hoạch trước khi code
- Phiên 2 (sạch): agent thực thi → Chị Lan: Kế hoạch + danh sách task
- Chị Lan → Phiên 2 (sạch): agent thực thi: Duyệt kế hoạch -> thực thi từng task
Vì sao phải mở phiên mới để thực thi? Phiên phỏng vấn chứa đầy những câu hỏi, câu trả lời lưỡng lự, ý tưởng bị bỏ — đó là “rác ngữ cảnh”. Nếu thực thi ngay trong phiên đó, agent có thể bị ảnh hưởng bởi một ý bạn đã gạt đi ở câu thứ 7. Phiên mới chỉ đọc spec đã duyệt: sạch, tập trung, và cũng là phép thử xem spec có thật sự tự chứa không. Nếu agent ở phiên mới phải hỏi lại nhiều điều, spec còn thiếu.
Spec là tài liệu sống
Spec không phải hợp đồng đóng dấu rồi cất tủ. Nó thay đổi khi bạn học thêm, và việc quản lý thay đổi đó quyết định spec còn đáng tin hay không.
- Nháp
- Đang review
- Đã duyệt
- Đang làm
- Đã triển khai
- phát hiện điều chưa rõ, sửa spec, tăng phiên bản
Xem sơ đồ gốc (Hình 3)
Vuốt ngang để xem hết sơ đồ
Đọc sơ đồ dạng chữ
- Bắt đầu → Nhap: tạo từ mẫu / agent phỏng vấn
- Nhap → DangReview: viết xong bản đầu
- DangReview → Nhap: cần sửa
- DangReview → DaDuyet: người dùng + chủ sở hữu đồng ý
- DaDuyet → DangLam: bắt đầu thực thi
- DangLam → DaDuyet: phát hiện điều chưa rõ, sửa spec, tăng phiên bản
- DangLam → DaTrienKhai: code khớp spec, kiểm chứng xong
- DaTrienKhai → DaDuyet: yêu cầu thay đổi mới
- DaTrienKhai → Kết thúc: lỗi thời / được thay thế
Năm thói quen giữ spec “sống” mà vẫn đáng tin:
- Lưu trong Git cùng code. Spec nằm ở
docs/specs/, mỗi thay đổi là một commit. Bạn luôn biết spec nói gì ở thời điểm một dòng code được viết. - Thay đổi hành vi bắt đầu từ spec. Hà muốn cho phép chữ trên bánh 50 ký tự? Sửa QT-01 trong spec, tăng phiên bản, ghi lịch sử — rồi mới bảo agent “cập nhật code theo spec phiên bản 1.2”.
- Khi code và spec mâu thuẫn, quyết định bên nào đúng. Đôi khi code đúng hơn (agent phát hiện trường hợp hợp lý) — khi đó cập nhật spec cho khớp. Không bao giờ để mâu thuẫn tồn tại.
- PR trỏ về spec. Mô tả mỗi pull request ghi “Hiện thực SPEC-001 v1.1, mục 3–6”. Người review đối chiếu được.
- Ghi trạng thái rõ ràng. Một spec “Đã triển khai” khác một spec “Nháp”; agent và người đọc cần biết mình đang cầm tài liệu nào.
Áp dụng vào dự án Bánh Mộc
Nhật ký công trình · Bánh Mộc
BM-261013-028
13.10.2026
- Chị LanChủ chuỗi
- HàBán hàng online
Chị Lan có sẵn nguyên liệu từ các bài trước: problem statement, sơ đồ as-is/to-be, MoSCoW, non-goals, 6 user story. Việc viết PRD gần như chỉ là ghép lại và cắt gọn; chị hoàn thành bản 1.0 trong một buổi tối, dài khoảng 2 trang.
Spec “Tạo đơn hàng” thì khác. Chị dùng Prompt 1 bên dưới với coding agent ở chế độ chỉ đọc. Agent đọc docs/PRD.md, docs/specs/user-stories.md và file mô tả mô hình dữ liệu, rồi hỏi 4 vòng, tổng cộng 17 câu. Vài câu chị chưa từng nghĩ tới:
- “Nếu hai nhân viên ở hai chi nhánh tạo đơn cùng một giây, mã đơn có thể trùng không? Chị muốn xử lý thế nào?”
- “Đơn tạo lúc 23:59 và nhận sau nửa đêm thì mã đơn theo ngày tạo hay ngày nhận?”
- “Giá bánh kem tùy chỉnh có phụ thu trang trí không? Nếu có, ai được nhập?”
- “Nếu khách đã tồn tại nhưng nhân viên gõ tên khác, có cập nhật tên khách không?”
Câu cuối khiến chị phải hỏi Hà, và cả hai thống nhất: không tự cập nhật tên khách khi tạo đơn (tránh ghi đè sai), việc sửa thông tin khách là tính năng riêng. Agent ghi điều này vào mục “Ngoài phạm vi”.
Sau khi có bản nháp, chị in ra, đọc cùng Hà. Hà phát hiện hai điều: khách hay gửi chữ kèm biểu tượng trái tim mà bếp không viết được (thành quy tắc cấm emoji), và Hà hay bấm nút hai lần khi mạng ở quầy chậm (thành QT-13). Spec lên phiên bản 1.1, trạng thái “Đã duyệt”.
Cuối cùng, chị mở phiên mới và dùng Prompt 2. Câu đầu tiên agent hỏi lại là “dự án đã có cấu hình Playwright chưa?” — một điều spec giả định nhưng không nói rõ. Chị bổ sung một dòng vào mục 12 và ghi lại bài học: phiên sạch là phép thử tốt nhất cho tính tự chứa của spec.
Prompt mẫu
Prompt 1 — Agent phỏng vấn rồi viết spec (phiên 1, chế độ chỉ đọc/lập kế hoạch)
Điền ô trống rồi sao chép
Tôi muốn xây tính năng "Tạo đơn hàng" cho Mộc OMS.
Trước tiên hãy đọc: docs/PRD.md, docs/specs/user-stories.md (US-01, US-02, US-03), docs/specs/_template.md,
và schema trong src/db/. Chưa viết code, chưa sửa file nào ngoài file spec ở bước cuối.
Sau đó PHỎNG VẤN tôi để viết spec chi tiết:
- Hỏi theo từng nhóm 3–5 câu, chờ tôi trả lời rồi mới hỏi nhóm tiếp.
- Không hỏi những gì đã có trong tài liệu; nếu tài liệu mâu thuẫn, hỏi tôi chọn bên nào.
- Hỏi cả những điểm khó tôi có thể chưa nghĩ tới: đồng thời (nhiều người cùng thao tác), múi giờ và qua nửa đêm,
phân quyền theo vai trò/chi nhánh, dữ liệu cũ không chuẩn, lỗi mạng và gửi trùng, giới hạn độ dài, tiền tệ.
- Với mỗi câu hỏi, nêu ngắn gọn vì sao nó quan trọng và đề xuất một lựa chọn mặc định hợp lý.
Khi tôi nói "đủ rồi", viết docs/specs/tao-don-hang.md theo đúng cấu trúc của _template.md:
- Nói rõ "cái gì" và "vì sao"; với "làm thế nào" chỉ nêu ràng buộc, file và interface liên quan.
- Có mục Ngoài phạm vi rõ ràng và mục Kiểm chứng end-to-end có lệnh và bằng chứng cần nộp.
- Điều chưa chắc đánh dấu [CẦN XÁC NHẬN] trong mục Câu hỏi mở, không tự quyết.
Prompt 2 — Thực thi spec ở phiên sạch (phiên 2)
Điền ô trống rồi sao chép
Đọc docs/specs/tao-don-hang.md (SPEC-001, phiên bản 1.1, trạng thái Đã duyệt) và các file nó tham chiếu.
Ở chế độ lập kế hoạch, chưa sửa code:
1. Liệt kê những điểm trong spec bạn thấy chưa rõ hoặc mâu thuẫn với codebase hiện tại. Nếu có, dừng lại hỏi tôi.
2. Đề xuất kế hoạch kỹ thuật, đặc biệt cách đảm bảo QT-09 (mã đơn không trùng khi tạo đồng thời)
và QT-13 (chống tạo trùng) — nêu 2 phương án và khuyến nghị.
3. Chia thành các task nhỏ, mỗi task kiểm chứng được độc lập, theo thứ tự: test trước, code sau.
Không làm bất cứ điều gì nằm trong mục "Ngoài phạm vi". Chờ tôi duyệt kế hoạch.
Prompt 3 — Cập nhật spec khi yêu cầu thay đổi
Điền ô trống rồi sao chép
Yêu cầu thay đổi: cho phép chữ trên bánh tối đa 50 ký tự (thay vì 40) với bánh từ 24cm trở lên.
1. Cập nhật docs/specs/tao-don-hang.md: sửa QT-01, mục 10 (thông báo lỗi), mục 11 nếu cần;
tăng phiên bản lên 1.2 và ghi một dòng vào Lịch sử thay đổi.
2. Liệt kê các file code và test sẽ phải đổi theo, nhưng CHƯA sửa code.
3. Kiểm tra xem các spec khác trong docs/specs/ có quy tắc liên quan cần cập nhật theo không (ví dụ màn hình bếp).
Sai lầm thường gặp
-
Sự cố 01
Viết spec như tiểu thuyết.
Triệu chứngDài, nhiều đoạn văn kể lể, quy tắc nằm lẫn trong câu chữ. *
Cách tránh* dùng mẫu có mục cố định; quy tắc đánh mã, mỗi quy tắc một dòng kiểm tra được.
-
Sự cố 02
Spec không tự chứa.
Triệu chứng“Như đã bàn hôm qua…”. *
Cách tránh* kiểm tra bằng phiên agent mới; mọi điều cần biết phải nằm trong spec hoặc file được trỏ tới.
-
Sự cố 03
Thiếu “Ngoài phạm vi”.
Triệu chứngAgent tiện tay làm thêm gửi email, trang quản trị. *
Cách tránh* mục 4 bắt buộc, kèm câu “không tự thêm tính năng ngoài danh sách”.
-
Sự cố 04
Trộn “cái gì” với “làm thế nào” quá sớm.
Triệu chứngSpec áp đặt cách cài đặt chi tiết, khóa chặt lựa chọn kỹ thuật chưa được cân nhắc. *
Cách tránh* spec nêu ràng buộc và kết quả; cách làm để bước plan đề xuất, bạn duyệt.
-
Sự cố 05
Thực thi ngay trong phiên phỏng vấn.
Triệu chứngNgữ cảnh lẫn ý đã bỏ. *
Cách tránh* luôn mở phiên sạch để thực thi spec đã duyệt.
-
Sự cố 06
Sửa code mà không sửa spec.
Triệu chứngSau vài tuần, spec thành tài liệu nói dối. *
Cách tránh* quy ước “thay đổi hành vi bắt đầu từ spec”; PR phải trỏ tới phiên bản spec.
Checklist
Bài tập thực hành
-
Bài 1
Cơ bản
PRD 2 trang
Viết PRD cho sản phẩm của bạn theo mẫu, tận dụng problem statement, MoSCoW, non-goals từ các bài trước.
Kết quả mong đợi
không quá 3 trang; một người ngoài đọc 10 phút trả lời đúng ba câu: giải vấn đề gì, cái gì ngoài phạm vi, thành công đo thế nào.
-
Bài 2
Nâng cao
Agent phỏng vấn
Dùng Prompt 1 cho một tính năng thật của bạn. Ghi lại các câu hỏi agent đặt ra mà bạn chưa từng nghĩ tới.
Kết quả mong đợi
một spec theo mẫu, ít nhất 3 câu hỏi “khó” được trả lời và phản ánh vào quy tắc, ít nhất 1 mục [CẦN XÁC NHẬN] mà bạn phải đi hỏi người làm thật.
-
Bài 3
Nâng cao
Phép thử phiên sạch
Mở một phiên agent mới, chỉ đưa spec ở bài 2 và yêu cầu: “Liệt kê mọi điều bạn cần hỏi trước khi có thể lập kế hoạch.”
Kết quả mong đợi
danh sách câu hỏi; bổ sung vào spec cho tới khi phiên mới gần như không cần hỏi thêm điều gì quan trọng. Tăng phiên bản và ghi lịch sử thay đổi.
Thuật ngữ trong bài
- PRD (Product Requirements Document)
- Tài liệu yêu cầu sản phẩm tổng thể: vấn đề, mục tiêu, người dùng, phạm vi.
- Spec tính năng
- Đặc tả chi tiết, tự chứa cho một tính năng.
- Tự chứa (self-contained)
- Đọc riêng tài liệu là đủ hiểu, không cần lịch sử trò chuyện.
- Spec-driven development
- Cách làm lấy spec làm nguồn sự thật: Specify → Plan → Tasks → Implement.
- Server action
- Hàm chạy phía máy chủ được gọi trực tiếp từ giao diện trong Next.js.
- Schema Zod
- Mô tả cấu trúc và ràng buộc dữ liệu để kiểm tra đầu vào.
- Idempotency (bất biến khi lặp)
- Gửi cùng một yêu cầu nhiều lần chỉ tạo ra một kết quả.
- Phiên sạch
- Phiên agent mới, không mang ngữ cảnh cũ.
- Tài liệu sống
- Tài liệu được cập nhật liên tục, có phiên bản và lịch sử thay đổi.
Đọc thêm
- Spec Kit (bộ công cụ spec-driven development mã nguồn mở): https://github.com/github/spec-kit
- Định dạng AGENTS.md: https://agents.md
- Viết prompt cho lập trình: rõ ràng, có bối cảnh, có tiêu chí kiểm chứng02-04
- Spec-driven development: lấy đặc tả làm nguồn sự thật06-04
- Case study: xây tính năng ‘Tạo đơn hàng’ cho Bánh Mộc từ A đến Z06-10
Nghiệm thu tờ 03-06
Đọc xong và tự kiểm theo phiếu nghiệm thu, rồi đánh dấu tờ này.

