Skip to content
Bùi Thúc Đồng Notebook · since 2009
§ 03 · TUTORIAL BEGINNER ~18 MIN UPDATED 2026-09-23

Đồng Use Claude — Phần 3: SPEC, đặc tả nghiệp vụ và luật chơi

Từ intent sang spec: functional requirement truy vết về bài toán, business rule viết thành luật rõ ràng, data schema, và trade-off có ghi ID để sau này đối chiếu. Kèm SPEC-01.md hoàn chỉnh cho app sổ khách.

INTENT trả lời “vì sao và cho ai”. SPEC trả lời “chính xác làm gì, theo luật nào”. Đây là file mà người không biết code thường bỏ qua — và cũng là file đáng giá nhất, vì 80% lỗi của app do AI làm không nằm ở code mà nằm ở luật nghiệp vụ không ai ghi ra.

Bài 3 trong series. Bài trước đã có INTENT-01.md cho app sổ khách; giờ ta dịch nó thành đặc tả.

Cấu trúc file SPEC

  1. Functional Requirements [FR-n] — mỗi chức năng một dòng, trỏ về [OUT-n]/[PRB-n] nó phục vụ. FR nào không trỏ về đâu = tính năng thừa, xoá.
  2. Business Rules [BR-n] — luật viết rõ, không diễn giải được hai chiều.
  3. Data Schema — bảng dữ liệu, trường nào bắt buộc, trường nào duy nhất.
  4. Trade-offs [TRD-n] — quyết định có đánh đổi, ghi rõ chọn cái gì và bỏ cái gì.
  5. Non-functional — hiệu năng, giới hạn, bảo mật ở mức cần thiết.

Quy tắc viết business rule: luật phải đủ rõ để hai người đọc ra cùng một hành vi. “Gộp khách trùng” không phải luật — gộp thế nào, field nào thắng, note cũ đi đâu? Luật đúng là: “Trùng phone → giữ bản ghi cũ, chỉ điền field còn trống, ghi chú mới nối vào ghi chú cũ.”

Ví dụ hoàn chỉnh: SPEC-01.md cho sổ khách

# Spec: sổ khách (mini-CRM)
Ref: intent/INTENT-01.md

## 1. Functional Requirements
- [FR-1] Form thêm khách: tên*, phone*, email, ghi chú
  → OUT-1
- [FR-2] Danh sách khách: tên, phone, ghi chú rút gọn,
  mới nhất lên đầu → OUT-1
- [FR-3] Trùng phone_norm → không tạo mới; gộp vào
  bản ghi cũ, trả về bản ghi đó → OUT-2
- [FR-4] Import nhiều khách một lúc (paste CSV) → báo
  số dòng thêm mới / gộp → OUT-2
- [FR-5] Xem chi tiết một khách → OUT-3

## 2. Business Rules
- [BR-1] phone_norm = chỉ giữ chữ số; đầu "+84" hoặc
  "84" đổi thành "0". VD: "+84 901-234" → "0901234"
- [BR-2] phone_norm là duy nhất toàn hệ thống — trùng
  tuyệt đối không tạo bản ghi thứ hai, kể cả hai
  request đến cùng lúc
- [BR-3] Gộp khi trùng: giữ bản ghi CŨ; chỉ điền các
  field đang trống; ghi chú mới nối sau ghi chú cũ
  (cách dòng); KHÔNG ghi đè tên cũ
- [BR-4] Tên khách để trống → từ chối, báo lỗi rõ
- [BR-5] Import: dòng thiếu phone → bỏ qua, đếm vào
  "skipped" trong báo cáo

## 3. Data Schema
customers:
| field       | type    | rule                    |
|-------------|---------|-------------------------|
| id          | integer | PK, tự tăng             |
| name        | text    | bắt buộc                |
| phone_norm  | text    | bắt buộc, UNIQUE [BR-2] |
| email       | text    | trống được              |
| note        | text    | trống được, nối khi gộp |
| created_at  | text    | ISO 8601                |

## 4. Trade-offs
- [TRD-1] Không xoá khách ở v1 — dữ liệu nghiệp vụ,
  xoá nhầm không cứu được. Bù: không có nút xoá.
- [TRD-2] Dedup chỉ theo phone, không theo tên/email —
  đơn giản, đúng 95% ca thật. Bỏ: 2 khách trùng tên
  vẫn là 2 bản ghi.
- [TRD-3] Không đăng nhập — app nội bộ. Chấp nhận:
  ai có link đều xem được. Ghi rõ trong rủi ro.

## 5. Non-functional
- Chạy trên Cloudflare Workers + D1 free tier
- List < 5.000 khách vẫn mở dưới 2 giây
- Form dùng được trên màn hình điện thoại 360px

Ba chỗ trong file này đáng chú ý:

  • [BR-2] khoá chuyện race condition ngay từ spec. “Kể cả hai request đến cùng lúc” là câu cố tình viết — nó ép kỹ thuật phải dùng UNIQUE constraint ở database thay vì check-then-insert, và nó cho ta kịch bản test ở bài 6.
  • [TRD-n] là nơi bạn thể hiện quyền Product Owner. AI sẽ không tự quyết trade-off cho bạn — nó sẽ chọn cái dễ code. Bạn chọn cái đúng nghiệp vụ rồi ghi vào.
  • FR ↔ OUT trace. FR-3 trỏ về OUT-2. Nếu bạn viết một FR không trỏ được về intent, hoặc bạn đang thêm tính năng thừa, hoặc intent thiếu — quay lại sửa file kia, đừng nhét vào.

Cách làm với Claude Code

> Đọc intent/INTENT-01.md. Viết spec/SPEC-01.md theo cấu
  trúc: FR (trỏ về OUT), BR (luật không diễn giải 2 chiều),
  Data Schema, Trade-offs, Non-functional. Chưa viết code.
  Chỗ nào cần tao quyết thì đánh dấu [?] thay vì tự chọn.

[?] là điểm mấu chốt: mặc định AI sẽ tự điền mọi quyết định. Đánh dấu hỏi buộc nó dừng lại ở đúng chỗ bạn phải làm việc. Sau khi nó xuất bản nháp, đi qua từng [?], quyết, sửa file, rồi mới qua bước PLAN.

Bài tập

  1. Viết spec/SPEC-01.md cho app của bạn từ file INTENT đã có.
  2. Kiểm truy vết: mọi FR phải trỏ về một OUT; mọi OUT phải có ít nhất một FR. Cái nào mồ côi → xoá hoặc quay lại sửa intent.
  3. Chọn 1 business rule quan trọng nhất, viết nó đủ rõ để kiểm được bằng tay: “nhập X, kỳ vọng Y”.
  4. Liệt kê tối thiểu 3 trade-off. Không có trade-off nào nghĩa là bạn chưa nhìn đủ kỹ.

Nguồn tham khảo

Bài tiếp theo: Phần 4 — PLAN.