111 lines
6.0 KiB
Markdown
111 lines
6.0 KiB
Markdown
# Ví dụ minh hoạ — `ba-3-specification`
|
||
|
||
Tách khỏi `SKILL.md` để quy trình không lẫn ví dụ của một ngành cụ thể. Mỗi bước minh hoạ ở
|
||
**nhiều domain và nhiều `PRODUCT`** — cùng một quy tắc, khác cách áp.
|
||
|
||
---
|
||
|
||
## Bước 3 · Bảng ràng buộc — cùng nguyên tắc, bốn hình dạng
|
||
|
||
Nguyên tắc chung: **giới hạn · mặc định · nguồn giá trị · hành vi khi sai** đều phải có.
|
||
|
||
### `screen` — bán lẻ, form tạo cửa hàng
|
||
|
||
| ID | Field | Kiểu | Bắt buộc | Giới hạn | Default | Nguồn giá trị | Validation | Message khi sai |
|
||
|---|---|---|---|---|---|---|---|---|
|
||
| F01 | Mã cửa hàng | Text | ✅ | 3–20 ký tự (code point UTF-8) | — | Người dùng nhập | `^[A-Z0-9-]+$`, không trùng | `E-STR-0001` |
|
||
| F02 | Loại | Chọn 1 | ✅ | — | "Thường" | API `/store-types`, lọc `active=true`, sắp theo `order` | thuộc danh sách | `E-STR-0002` |
|
||
|
||
### `api-service` — y tế, đặt lịch khám
|
||
|
||
| Tên | Kiểu | Vị trí | Bắt buộc | Mặc định | Ràng buộc | BR |
|
||
|---|---|---|---|---|---|---|
|
||
| `patientId` | string | body | ✅ | — | 19 chữ số, **string vì vượt 2^53** | BR-004 |
|
||
| `slotStart` | string | body | ✅ | — | ISO-8601 UTC, phải ≥ now+2h, thuộc giờ làm việc của phòng khám | BR-011 |
|
||
| `channel` | enum | body | ❌ | `WEB` | `WEB\|APP\|CALL` — giá trị lạ ⇒ 400, **không âm thầm bỏ qua** | BR-012 |
|
||
|
||
### `data-pipeline` — logistics, nạp sự kiện quét kho
|
||
|
||
| Trường đích | Từ nguồn | Phép biến đổi | Nguồn null | Nguồn sai định dạng |
|
||
|---|---|---|---|---|
|
||
| `warehouse_code` | `wh.id` | `upper(trim(x))` | → `UNKNOWN`, đếm vào DQ-02 | → quarantine |
|
||
| `scanned_at` | `ts` | epoch ms → timestamp UTC | 🛑 dừng luồng (không suy ra được) | → quarantine |
|
||
| `qty` | `quantity` | ép số nguyên | → 0 | → quarantine |
|
||
|
||
### `ml-model` — tài chính, chấm điểm rủi ro khoản vay
|
||
|
||
| ID | Metric | Đo trên | Ngưỡng | Baseline | Truy về chi phí nghiệp vụ |
|
||
|---|---|---|---|---|---|
|
||
| M-01 | Precision @ 0.7 | Test giữ lại | ≥ 0,85 | Quy tắc tay: 0,62 | Mỗi FP tốn ~25 phút thẩm định tay |
|
||
| M-02 | Recall | Test giữ lại | ≥ 0,70 | 0,45 | Mỗi FN ≈ 40 triệu nợ xấu trung bình |
|
||
|
||
🔴 Cột cuối là cột phân biệt một ngưỡng có căn cứ với một con số ai đó thấy đẹp.
|
||
|
||
---
|
||
|
||
## Bước 4 · AC bốn nhóm — ví dụ nhóm 3 (lỗi hệ thống)
|
||
|
||
Nhóm hay bị bỏ nhất, ở mọi domain.
|
||
|
||
**Y tế · `screen` · form đặt lịch:**
|
||
```
|
||
Given tôi đã điền đầy đủ form đặt lịch hợp lệ
|
||
When tôi bấm Xác nhận và request bị timeout sau 30 giây
|
||
Then hiện thông báo "Không kết nối được, vui lòng thử lại"
|
||
And nút Xác nhận bấm lại được
|
||
And 🔴 toàn bộ thông tin tôi đã nhập được giữ nguyên
|
||
And 🔴 nếu lịch đã được tạo ở phía server, lần bấm lại KHÔNG tạo lịch thứ hai
|
||
```
|
||
|
||
Dòng cuối là chỗ AC nhóm 3 gặp `api-service`: cần idempotency key, và nó phải nằm trong spec
|
||
chứ không phải để dev tự nghĩ ra.
|
||
|
||
**Logistics · `batch-job` · job đối soát tồn kho đêm:**
|
||
```
|
||
Given job đang xử lý dở 12.000/50.000 bản ghi
|
||
When tiến trình bị ngắt
|
||
Then lần chạy tiếp theo bắt đầu từ checkpoint bản ghi 12.000
|
||
And không bản ghi nào bị xử lý hai lần
|
||
And không bản ghi nào bị bỏ sót
|
||
```
|
||
|
||
---
|
||
|
||
## Bước 5 · Phân biệt "không có gì" với "không lấy được"
|
||
|
||
Cùng một lỗi tư duy, bốn biểu hiện:
|
||
|
||
| Domain · `PRODUCT` | ❌ Gộp làm một | ✅ Phân biệt |
|
||
|---|---|---|
|
||
| Bán lẻ · `screen` | Cả hai đều hiện "Không có dữ liệu" | "Chưa có bản ghi nào" *(nút Tạo mới)* ↔ "Không khớp bộ lọc" *(nút Xoá lọc)* |
|
||
| Y tế · `api-service` | Cả hai trả 404 | Không còn slot trống: `200` + `[]` ↔ phòng khám không tồn tại: `404` |
|
||
| Logistics · `data-pipeline` | Cả hai nạp 0 bản ghi, báo thành công | Chủ nhật không phát sinh: 0 bản ghi + nhãn "không có hoạt động" ↔ API kho không phản hồi: 🔴 cảnh báo |
|
||
| Tài chính · `batch-job` | Job báo "thành công, 0 bản ghi" | Không có giao dịch cần đối soát ↔ job trước chưa xong nên chưa có đầu vào |
|
||
|
||
Ba dòng cuối là chế độ hỏng **im lặng**: hệ thống báo thành công trong khi dữ liệu không tới.
|
||
|
||
---
|
||
|
||
## Bước 6 · NFR có số đo — ba domain
|
||
|
||
| ❌ Khẩu hiệu | ✅ Yêu cầu |
|
||
|---|---|
|
||
| "Màn hình danh sách phải nhanh" | "Trả về ≤ 2s (p95) với 100.000 bản ghi và 20 người dùng đồng thời, trên staging. Verify: k6 kịch bản S1 trước mỗi release" |
|
||
| "Dữ liệu phải cập nhật kịp thời" | "Dữ liệu ngày D sẵn sàng trước D+1 08:00. Trễ > 2h ⇒ cảnh báo cho nhóm vận hành. Người dùng thấy nhãn 'dữ liệu tới <ngày>' khi chưa có số mới" |
|
||
| "Mô hình phải chạy nhanh" | "Dự đoán đồng bộ ≤ 300ms (p99) cho 1 hồ sơ; quá hạn ⇒ trả fallback 'cần thẩm định tay' và ghi log" |
|
||
|
||
---
|
||
|
||
## Bẫy "bịa giá trị nghe hợp lý"
|
||
|
||
Bốn con số này xuất hiện ở mọi domain và gần như luôn là bịa:
|
||
|
||
| Con số | Vì sao nguy hiểm | Thay bằng |
|
||
|---|---|---|
|
||
| `255 ký tự` | Là giới hạn mặc định của DB, không phải yêu cầu nghiệp vụ | `OQ`: tên dài nhất thực tế là bao nhiêu? |
|
||
| `timeout 30 giây` | Chọn theo thói quen | `OQ`: người dùng chờ bao lâu thì bỏ cuộc? |
|
||
| `giữ log 90 ngày` | Nghe hợp lý nên không ai chất vấn | `OQ`: quy định lưu trữ của tổ chức là gì? |
|
||
| `precision ≥ 0.85` | Con số tròn, trông chuyên nghiệp | `OQ`: mỗi lần sai tốn bao nhiêu? |
|
||
|
||
Chúng nguy hiểm chính vì **nghe hợp lý** — không ai chất vấn, và sai thì phát hiện rất muộn.
|