Spec-driven development với AI: viết plan trước khi code (2026)
Spec-driven development (SDD) là cách làm lấy đặc tả (spec) làm nguồn sự thật duy nhất: bạn viết spec và plan có tiêu chí nghiệm thu trước, rồi mới để AI agent sinh code và test theo đó. So với "vibe-only" (ném một câu prompt cụt rồi hy vọng), SDD giảm mạnh chuyện AI code lệch yêu cầu, trôi kiến trúc và đốt token vào những vòng sửa đi sửa lại. Bài này cho bạn một spec.md + plan.md mẫu thật copy-dùng-được, và cách chạy nó ngay trong Claude Code.
Spec-driven development là gì?
Spec-driven development là phương pháp lấy đặc tả (spec) làm nguồn sự thật duy nhất: bạn viết ra "cần làm gì, tại sao, và thế nào là xong" trước khi viết dòng code đầu tiên, rồi để AI agent sinh code, test và tài liệu bám theo đặc tả đó. Nói ngắn gọn: đặc tả dẫn dắt AI, chứ không phải AI đoán ý bạn.
Điểm cốt lõi nằm ở chữ "nguồn sự thật duy nhất". Trong lối làm cũ, yêu cầu nằm rải rác trong đầu bạn, trong vài dòng chat, trong một ticket sơ sài. AI đọc được rất ít trong số đó nên nó phải suy diễn phần còn lại - và suy diễn là nơi lỗi sinh ra. SDD ép bạn gom mọi ràng buộc quan trọng vào một tài liệu mà cả người lẫn agent đều đọc được: mục tiêu, phạm vi, tiêu chí nghiệm thu (acceptance criteria), và cả những thứ cố tình không làm (non-goals).
Đây không phải quay lại thời "tài liệu dày cộp trước khi code" của waterfall. Spec trong SDD gọn, sống động, và thường chỉ dài một tới hai màn hình. Nó là context before code - bối cảnh đủ để một agent thông minh làm đúng ngay lần đầu, thay vì bạn phải sửa tay ba bốn vòng. Thoughtworks gọi đây là một pattern đang định hình lại cách viết phần mềm với AI (Thoughtworks, 2025).
Vì sao viết plan trước lại thắng "vibe-only"?
Hãy gọi thẳng tên vấn đề của vibe-only bằng một khái niệm dễ nhớ: "ambiguity tax" - thuế mơ hồ. Mỗi chỗ yêu cầu của bạn còn mập mờ, AI buộc phải tự điền vào khoảng trống. Nó điền bằng phỏng đoán trung bình từ dữ liệu huấn luyện, không phải bằng ý định thật của bạn. Kết quả là bạn trả thuế đó dưới dạng những vòng sửa: "không, ý anh không phải vậy", "thiếu case này", "sao lại đổi cả file kia".
Nếu bạn mới nghe tới lối làm buông tay để AI dẫn, hãy đọc trước vibe coding là gì để có bối cảnh - SDD chính là bước kỷ luật hóa vibe coding, không phải phủ nhận nó. Vibe-only phù hợp cho việc dò dẫm nhanh; nhưng khi task có yêu cầu rõ, nó lộ ba rủi ro:
- Lệch yêu cầu: AI làm ra thứ chạy được nhưng không đúng cái bạn cần - thiếu edge case, hiểu sai luồng nghiệp vụ, đặt tên/giao diện lệch quy ước.
- Trôi kiến trúc: mỗi prompt là một quyết định thiết kế ngẫu hứng. Sau mười prompt, codebase thành một mớ chắp vá không ai chủ đích thiết kế.
- Phình chi phí token: mỗi vòng "sửa lại giúp mình" là một lần agent đọc lại context, sinh lại code. Ba bốn vòng rework tốn token gấp bội so với một spec tốt ngay từ đầu.
Spec-driven đảo ngược thứ tự: bạn trả "chi phí suy nghĩ" một lần, ở đầu, khi nó còn rẻ. Viết ra acceptance criteria buộc bạn tự trả lời những câu mà lẽ ra AI sẽ phải đoán. Khi context đã rõ, agent gần như không còn khoảng trống để phỏng đoán sai. Đây cũng là lý do một quy trình vibe coding trưởng thành luôn có bước viết plan xen vào giữa, thay vì prompt liên tục.
Spec vs Plan vs Task - khác nhau thế nào?
Ba từ này hay bị gộp mờ, nhưng phân biệt rạch ròi chúng là chìa khóa của SDD. Ngắn gọn: spec trả lời "cái gì & tại sao", plan trả lời "làm thế nào, theo thứ tự", còn task là đơn vị thực thi nhỏ nhất.
| Yếu tố | Trả lời câu hỏi | Chứa gì | Ai đọc chính |
|---|---|---|---|
| Spec | Cái gì & tại sao | Mục tiêu, phạm vi, acceptance criteria, non-goals, edge case | Người + AI cùng duyệt |
| Plan | Làm thế nào, theo thứ tự | Các bước có thứ tự, file sẽ đụng tới, cách test, rủi ro/rollback | AI agent thực thi |
| Task | Việc cụ thể tiếp theo | Một đơn vị nhỏ, làm xong & kiểm được trong một lượt | Agent (hoặc bạn) làm từng cái |
Nhầm lẫn thường gặp: nhét cách làm vào spec (spec bị "cầm tay chỉ việc" quá sớm, mất linh hoạt), hoặc viết plan mà bỏ acceptance criteria (agent không biết khi nào được coi là xong). Một mẹo giữ ranh giới: nếu một câu trả lời cho "người dùng/hệ thống cần gì" thì nó thuộc spec; nếu trả lời cho "ta gõ gì, sửa file nào trước" thì nó thuộc plan.
Ví dụ THẬT: một spec + plan trước khi code
Lý thuyết đủ rồi. Đây là hiện vật thật cho một feature nhỏ mình hay dùng để minh họa: thêm rate-limit cho endpoint đăng nhập. Bạn copy hai file này, sửa vài dòng cho hợp dự án là dùng được. Trước hết là spec.md - chỉ nói "cái gì & tại sao", không nói cách làm:
# spec.md - Rate limit cho API login
## Goal
Chặn brute-force vào POST /api/login bằng cách giới hạn số lần thử
theo IP + email, trả về lỗi rõ ràng khi vượt ngưỡng.
## Tại sao
Hiện login không có giới hạn → dễ bị dò mật khẩu và làm quá tải DB.
## Acceptance criteria
- Tối đa 5 lần thử sai / 15 phút cho mỗi cặp (IP, email).
- Vượt ngưỡng → HTTP 429 + body { error: "too_many_attempts", retry_after }.
- Đăng nhập ĐÚNG sẽ reset bộ đếm cho cặp (IP, email) đó.
- Có test tự động cho: dưới ngưỡng, đúng ngưỡng, vượt ngưỡng, và reset.
## Non-goals
- KHÔNG làm CAPTCHA (để phase sau).
- KHÔNG rate-limit các endpoint khác trong lần này.
## Edge cases
- Nhiều user sau cùng một NAT/IP → khóa theo (IP, email), không chỉ IP.
- Đồng hồ/timezone: dùng UTC cho cửa sổ thời gian.
Tiếp theo là plan.md - giờ mới nói "làm thế nào, theo thứ tự". Chú ý cột file đụng tới và phần rollback:
# plan.md - Thực thi rate limit login
## Các bước (theo thứ tự)
1. Thêm store đếm lần thử (Redis, key = login:{ip}:{email}, TTL 15p).
→ File: src/lib/rate-limit.ts (mới)
2. Viết middleware checkLoginRateLimit đọc/tăng bộ đếm.
→ File: src/middleware/login-rate-limit.ts (mới)
3. Gắn middleware vào route POST /api/login TRƯỚC handler xác thực.
→ File: src/routes/auth.ts (sửa)
4. Khi login đúng → xóa key đếm của cặp (IP, email).
→ File: src/routes/auth.ts (sửa)
5. Viết test 4 case trong acceptance criteria.
→ File: tests/login-rate-limit.test.ts (mới)
## Cách test
- npm test tests/login-rate-limit.test.ts
- Thủ công: gửi 6 request sai liên tiếp → request thứ 6 phải trả 429.
## Rủi ro & rollback
- Redis down → fail-open (cho qua) hay fail-closed? Chọn fail-open + log
cảnh báo để không tự khóa toàn bộ user khi hạ tầng lỗi.
- Rollback: gỡ middleware ở bước 3 là hệ thống về nguyên trạng.
Kết quả khi để AI chạy theo plan này: agent làm đúng thứ tự, tạo đủ file, và tự dừng đúng chỗ vì acceptance criteria đã nói rõ "xong nghĩa là gì". Không còn cảnh nó tiện tay refactor cả module auth hay quên case reset bộ đếm. Cùng một agent, cùng một task - khác biệt nằm ở việc nó có bản đồ hay không.
Quy trình spec-driven với AI (6 bước)
Đây là vòng lặp mình dùng cho gần như mọi feature vừa-và-lớn. Nó ánh xạ gần như một-một với quy trình brainstorm → plan → cook → ship:
- Ý tưởng & bối cảnh: nêu vấn đề cần giải và ràng buộc thực tế (stack, quy ước, thứ không được đụng). Đây là lúc gom "sự thật" của dự án.
- Viết spec: điền Goal / Non-goals / Acceptance criteria / Edge cases. Ép mình cụ thể ở tiêu chí nghiệm thu - mơ hồ chỗ nào, AI đoán chỗ đó.
- Review spec (người + AI): nhờ agent đọc spec và chỉ ra chỗ mâu thuẫn, thiếu case, hoặc yêu cầu bất khả thi - trước khi có một dòng code nào.
- Viết plan & chia task: biến spec thành các bước có thứ tự, ghi rõ file đụng tới, cách test và rollback. Chia nhỏ tới mức mỗi task kiểm được trong một lượt.
- Để agent code theo task: chạy từng task, không nhảy cóc. Sau mỗi task, để agent tự đối chiếu với plan.
- Verify theo acceptance criteria: chạy test và soi từng dòng tiêu chí. Chỉ khi mọi tiêu chí xanh thì feature mới coi là xong - không phải khi "trông có vẻ chạy".
Mấu chốt: bước 3 và bước 6 mới là nơi SDD tiết kiệm cho bạn nhiều nhất. Bắt lỗi ở spec rẻ hơn bắt lỗi ở code hàng chục lần.
Làm spec-driven ngay trong Claude Code
Bạn không cần công cụ đặc biệt để bắt đầu - Claude Code có sẵn ba thứ đủ dựng một vòng SDD gọn:
- Plan Mode: Claude Code lập kế hoạch và cho bạn duyệt trước khi đụng vào file - đúng tinh thần "plan trước, code sau" (Anthropic docs, 2026). Xem chi tiết cách khai thác ở bài lập kế hoạch với Claude Code (Plan Mode).
- CLAUDE.md làm guardrail thường trực: đặt quy ước, ranh giới và non-goals dài hạn vào file này để agent luôn đọc - nó biến những ràng buộc lặp lại thành "sự thật" cố định của repo. Đây chính là context engineering ở dạng đơn giản nhất.
- GitHub Spec Kit: bộ lệnh mã nguồn mở đưa SDD thành workflow rõ ràng
/specify→/plan→/tasks, dùng được với Claude Code và nhiều agent khác (GitHub Blog, 2025).
Muốn quy trình spec-driven dựng sẵn? Nếu bạn ngại tự chắp CLAUDE.md + Plan Mode + Spec Kit, có bộ bộ kit AgentKit (giảm 20% qua link) cho Claude Code đóng gói sẵn các skill brainstorm/plan/cook/ship và subagent review theo đúng mạch spec → plan → code → verify. Mình có review chi tiết ở AgentKit là gì - đọc để tự cân nhắc, không cần vội mua.
Công cụ spec-driven 2026
Vài lựa chọn phổ biến, từ nhẹ tới đóng gói sẵn:
| Công cụ | Điểm mạnh | Phù hợp ai |
|---|---|---|
| GitHub Spec Kit (OSS) | Workflow rõ ràng /specify /plan /tasks; miễn phí, dùng với nhiều agent | Ai muốn quy ước SDD chuẩn, không phụ thuộc một IDE |
| Kiro IDE (AWS) | IDE spec-first, sinh spec/design/task ngay trong editor | Ai thích một môi trường tích hợp trọn gói |
| Claude Code + Plan Mode/CLAUDE.md | Không cần cài thêm; guardrail thường trực; duyệt plan trước khi code | Người đã dùng Claude Code, muốn bắt đầu ngay |
| AgentKit workflow | Đóng gói mạch brainstorm→plan→cook→ship + subagent review | Ai muốn quy trình dựng sẵn thay vì tự chắp |
Không có công cụ "đúng nhất". Markdown thuần + Plan Mode đã đủ cho phần lớn feature; các bộ nặng hơn chỉ đáng khi bạn làm SDD thường xuyên và muốn chuẩn hóa cho cả team.
Khi nào KHÔNG cần spec-driven?
SDD là công cụ, không phải tôn giáo. Ép spec vào mọi thứ sẽ phản tác dụng. Bỏ qua SDD khi:
- Script chạy một lần hoặc tác vụ throwaway - viết spec còn lâu hơn tự làm.
- Prototype/spike thăm dò: mục tiêu là học nhanh, chưa phải làm đúng. Vibe-only hợp hơn ở giai đoạn này.
- Sửa bug một dòng đã hiểu rõ nguyên nhân - không cần acceptance criteria cho việc đổi một ký tự.
- Yêu cầu còn thay đổi liên tục từng giờ: spec sẽ lỗi thời nhanh hơn bạn viết.
Hai cái bẫy cần cảnh giác ngay cả khi SDD phù hợp: over-spec (viết đặc tả chi li tới mức cứng nhắc, giết linh hoạt) và spec rot (spec không được cập nhật khi code đổi, thành tài liệu nói dối). Spec tốt là spec đủ để agent làm đúng và vẫn dễ sửa - không phải spec dài nhất.
Câu hỏi thường gặp (FAQ)
Spec-driven development khác vibe coding thế nào?
Vibe coding là ném prompt và để AI dẫn, hợp cho dò dẫm nhanh. Spec-driven đặt một đặc tả có tiêu chí nghiệm thu làm nguồn sự thật trước khi code, hợp cho feature có yêu cầu rõ. SDD là bước kỷ luật hóa vibe coding, không phủ nhận nó.
Spec khác plan không?
Khác. Spec trả lời "cái gì & tại sao" (mục tiêu, phạm vi, acceptance criteria, non-goals). Plan trả lời "làm thế nào, theo thứ tự" (các bước, file đụng tới, cách test, rollback). Spec ổn định hơn; plan có thể đổi khi cách làm đổi.
Có cần công cụ riêng không hay markdown là đủ?
Markdown thuần là đủ để bắt đầu - chỉ cần một spec.md và một plan.md. Các công cụ như GitHub Spec Kit hay Kiro chỉ giúp chuẩn hóa quy trình khi bạn làm SDD thường xuyên hoặc theo team.
Spec-driven có làm chậm không?
Chậm ở đầu, nhanh ở tổng thể. Bạn tốn thêm ít phút viết spec nhưng cắt được nhiều vòng rework khi AI code lệch. Với task nhỏ/throwaway thì đúng là không đáng - khi đó cứ vibe.
Dùng spec-driven với Cursor/Copilot được không?
Được. SDD là phương pháp, không gắn với một công cụ. Bạn có thể để spec.md/plan.md trong repo và dùng bất kỳ agent nào (Claude Code, Cursor, Copilot) đọc theo. GitHub Spec Kit vốn thiết kế để dùng đa agent.
Spec dài bao nhiêu là đủ?
Đủ để agent không phải đoán những gì quan trọng, thường một tới hai màn hình. Nếu spec dài hơn code nó tạo ra, bạn đang over-spec. Tiêu chí thật là: có acceptance criteria rõ và non-goals rõ.
Kết luận + bước tiếp theo
Nguyên tắc gọn trong bốn chữ: spec trước, code sau. Bạn trả chi phí suy nghĩ một lần ở đầu - nơi nó rẻ nhất - để AI khỏi trả thuế mơ hồ bằng những vòng sửa tốn kém. Bước tiếp: đọc quy trình brainstorm → plan → cook → ship để thấy SDD nằm ở đâu trong một vòng làm việc đầy đủ, và lập kế hoạch với Claude Code (Plan Mode) để bắt tay làm ngay.
Muốn Claude Code mạnh hơn ngay? Nếu bạn muốn mạch spec → plan → code → verify được dựng sẵn thành skill và subagent thay vì tự chắp từng mảnh, AgentKit cho Claude Code (agentkit.best, CLI ak) đóng gói đúng quy trình đó - có Money-back guarantee và cập nhật trọn đời cho kit.