·

Tiếng Việt: Translating Business Requirements Into Engineering Specs

Translating Business Requirements Into Engineering Specs

Một dòng Jira ticket có thể sinh ra ba cách hiểu khác nhau — và nếu bạn để AI agent tự đoán, nó sẽ chọn cách hiểu sai nhanh và tự tin nhất.

Chuỗi Dịch Thuật: Từ Business Ask Đến Agent Task

Mọi tính năng đều bắt đầu từ một câu nói rất người: một tin nhắn Slack của PM ("mình nghĩ nên cho user lưu sản phẩm lại để mua sau"), một dòng trong Jira ("Add save-for-later to cart"), hoặc một câu trong buổi họp roadmap. Đó là business ask — thứ mơ hồ, ngắn gọn, và đầy ngữ cảnh ngầm định mà người nói cho là "ai cũng hiểu".

Từ câu nói đó đến dòng code chạy được, có một chuỗi dịch thuật (translation chain) gồm nhiều bước, và ở mỗi bước, thông tin bị mất đi hoặc bị thêm vào một cách âm thầm:

  1. Business ask → câu nói / ticket một dòng. Chứa ý định (intent) nhưng thiếu chi tiết.
  2. PRD (Product Requirements Document) → PM diễn giải ý định thành mục tiêu, user story, đôi khi kèm vài "acceptance criteria" sơ sài. Ở bước này, PM đã tự điền vào chỗ trống bằng giả định của riêng họ — thường là giả định đúng cho "happy path" nhưng bỏ sót toàn bộ edge case.
  3. Spec kỹ thuật → engineer (hoặc trước đây là bạn, giờ có thể là bạn + AI) diễn giải PRD thành hành vi hệ thống cụ thể: input/output, trạng thái dữ liệu, ràng buộc, lỗi cần xử lý.
  4. Acceptance criteria → danh sách điều kiện đo lường được để biết "xong" là gì.
  5. Agent task → prompt hoặc task cụ thể đưa cho AI coding agent, đủ chi tiết để nó viết code đúng mà không cần đoán.

Vấn đề nằm ở chỗ: mỗi hop trong chuỗi này đều do một người (hoặc một AI) diễn giải lại theo hiểu biết riêng của họ, và không ai có full context của người trước. PM viết PRD dựa trên hiểu biết nghiệp vụ nhưng không biết hệ thống thanh toán hiện tại xử lý race condition thế nào. Engineer viết spec dựa trên hiểu biết hệ thống nhưng có thể hiểu sai ý định nghiệp vụ của PM. Và nếu bạn đưa thẳng PRD (hoặc tệ hơn, đưa thẳng câu Slack gốc) cho AI agent, nó sẽ "làm đầy" mọi khoảng trống bằng suy luận thống kê — tức là nó đoán theo pattern phổ biến nhất trong dữ liệu huấn luyện, không phải theo ngữ cảnh dự án của bạn.

Đây chính là lý do engineer — không phải PM, không phải AI — phải là người sở hữu (own) bước dịch từ PRD sang spec kỹ thuật. Bạn là người duy nhất trong chuỗi vừa hiểu ý định nghiệp vụ, vừa hiểu ràng buộc hệ thống, vừa hiểu AI agent cần loại thông tin nào để không phải đoán. Nếu bạn bỏ qua bước này và giao thẳng "yêu cầu thô" cho agent, bạn không tiết kiệm thời gian — bạn chỉ đang dời việc sửa lỗi từ trước khi code sang sau khi code, lúc đó chi phí sửa đã cao hơn nhiều.

Một cách hình dung thực tế: agent thực thi chính xác những gì bạn viết, không phải những gì bạn nghĩ. Khoảng cách giữa hai điều đó chính là công việc của bạn khi viết spec.

Mẹo: Vẽ chuỗi dịch thuật này ra cho chính dự án của bạn (business ask → PRD → spec → acceptance criteria → agent task) và tự hỏi: bước nào hiện đang bị bỏ qua? Đội nào hay skip thẳng từ ticket sang code thường sẽ thấy lỗi tập trung ở đúng bước bị bỏ qua đó.

Ba Lỗ Hổng Thường Gặp: Ambiguity, Contradiction Và Assumed Context

Ba loại lỗ hổng phổ biến nhất khi yêu cầu nghiệp vụ đi qua chuỗi dịch thuật là ambiguity (mơ hồ), contradiction (mâu thuẫn), và assumed context (ngữ cảnh ngầm định). Hãy xem chúng qua một yêu cầu tưởng chừng đơn giản: "Add a 'save for later' button to the cart."

Ambiguity — mơ hồ về hành vi. Câu yêu cầu không nói rõ:
- "Save for later" nghĩa là item biến mất khỏi giỏ hàng hiện tại, hay vẫn hiển thị nhưng ở trạng thái khác?
- Item được lưu ở đâu — gắn với account, hay gắn với session/device?
- Guest user (chưa đăng nhập) có được dùng tính năng này không?
- Có giới hạn số lượng item được lưu không?

Mỗi câu hỏi trên đều có nhiều câu trả lời hợp lý, và nếu không hỏi, engineer (hoặc agent) sẽ chọn một câu trả lời — thường là câu dễ implement nhất, không phải câu đúng nhất với nghiệp vụ.

Contradiction — mâu thuẫn giữa các phần của yêu cầu, hoặc giữa yêu cầu mới và hệ thống hiện tại. Giả sử PRD viết thêm: "Saved items should persist even if the user clears their cart" nhưng đồng thời hệ thống giỏ hàng hiện tại lưu toàn bộ cart (bao gồm cả saved items) trong một object duy nhất bị xóa sạch khi user bấm "clear cart". Đây là mâu thuẫn trực tiếp giữa kỳ vọng nghiệp vụ mới và kiến trúc dữ liệu hiện có — nếu không phát hiện trước, engineer sẽ implement xong rồi mới nhận ra "clear cart" đang xóa luôn saved items, phải làm lại data model.

Assumed context — ngữ cảnh người viết yêu cầu cho là hiển nhiên nên không nói ra. PM viết yêu cầu này đang nghĩ trong đầu về một trang giỏ hàng desktop, không nghĩ tới mobile app có luồng khác. Hoặc PM giả định rằng "save for later" chỉ áp dụng cho sản phẩm còn hàng (in stock) — nhưng không viết ra vì với họ điều đó "dĩ nhiên". Engineer đọc yêu cầu không có ngữ cảnh đó sẽ không biết cần xử lý case sản phẩm hết hàng khi user bấm "move to cart" từ saved list.

Một ví dụ thứ hai để thấy rõ pattern lặp lại: "Support guest checkout."
- Ambiguity: guest checkout có được tạo tài khoản ẩn (shadow account) để sau này merge lịch sử mua hàng không, hay hoàn toàn không lưu gì?
- Contradiction: PRD ghi "guest checkout is faster, no signup required" nhưng đồng thời yêu cầu "must collect email for order confirmation and marketing opt-in" — bước thu thập email + opt-in checkbox chính là một hình thức đăng ký thu nhỏ, mâu thuẫn với tinh thần "no signup".
- Assumed context: PM giả định discount code, loyalty points, và saved payment methods — vốn gắn với account — sẽ "tự động không áp dụng" cho guest, nhưng không ghi rõ hệ thống hiện tại có check được điều đó không, hay sẽ throw lỗi khi guest cố áp dụng loyalty points.

Điểm chung: cả ba loại lỗ hổng đều không xuất hiện dưới dạng "thiếu thông tin rõ ràng" — chúng trông giống như yêu cầu đã đầy đủ, cho tới khi bạn thử viết acceptance criteria chi tiết hoặc thử liệt kê edge case. Đó chính là lý do bước tiếp theo — dùng AI để "thẩm vấn" yêu cầu — lại hữu ích: nó buộc bạn liệt kê ra những case cụ thể, và chính hành động liệt kê đó phơi bày lỗ hổng.

Mẹo: Khi đọc một yêu cầu nghiệp vụ, thử viết lại nó dưới dạng 5-7 câu "Given/When/Then". Nếu bạn không viết nổi một câu "Then" cụ thể cho một "When" nào đó, đó chính là dấu hiệu của ambiguity hoặc assumed context.

Dùng AI Để "Thẩm Vấn" Yêu Cầu Nghiệp Vụ

Kỹ thuật cốt lõi ở đây không phải là dùng AI để viết spec ngay — mà dùng AI như một người thẩm vấn kiểu Socratic (Socratic interrogator): đưa yêu cầu thô vào, để AI đặt câu hỏi ngược lại bạn, thay vì để AI tự trả lời và tự viết spec luôn.

Lý do cách này hiệu quả: AI không có ngữ cảnh nghiệp vụ hay ngữ cảnh hệ thống của bạn, nhưng nó rất giỏi nhận diện pattern — nó "đã thấy" hàng nghìn yêu cầu tương tự trong dữ liệu huấn luyện và biết những câu hỏi nào thường bị bỏ sót. Việc của bạn là trả lời các câu hỏi đó bằng kiến thức thật (PM/domain knowledge) mà AI không có. Kết quả là một bản spec được xây từ câu trả lời thật của bạn, không phải từ suy đoán của AI.

Quy trình gồm hai bước tách biệt: (1) liệt kê edge case, (2) phơi bày giả định ngầm. Tách riêng vì chúng nhắm vào hai loại lỗ hổng khác nhau — edge case nhắm vào ambiguity/contradiction ở tầng hành vi, còn context probe nhắm vào assumed context ở tầng tiền đề.

Step 1: Trích Xuất Edge Case Từ Một Yêu Cầu

Đưa yêu cầu thô vào một prompt được thiết kế để liệt kê toàn diện các edge case, phân theo nhóm để dễ rà soát:

You are a senior engineer reviewing a raw product requirement before it
becomes an engineering spec. Do NOT propose a solution or write any code.

Raw requirement:
"Add a 'save for later' button to the cart. When clicked, the item
moves out of the active cart into a saved list the user can access later."

Task: Enumerate edge cases this requirement does not address, grouped by:
1. State transitions (what happens to the item/cart/order state)
2. User identity (guest vs. logged-in, multi-device)
3. Data lifecycle (expiration, limits, persistence across sessions)
4. Concurrency (multiple tabs, race conditions)
5. Interaction with adjacent features (pricing, inventory, promotions)

For each edge case, phrase it as a specific question I need to answer,
not as a suggested behavior. List 3-5 questions per group.

Output mẫu mà AI có thể trả về:

1. State transitions
   - Does the item disappear from the cart immediately, or show a
     transitional "moved to saved" state before disappearing?
   - If the item was the last one in the cart, does the cart become
     "empty" or does the UI still show the saved-items section?
   - What happens to any applied discount code if it required a
     minimum cart value that the item removal now drops below?

2. User identity
   - Can a guest (not logged in) use "save for later," or does clicking
     it prompt a login/signup flow?
   - If a guest saves an item and later logs in, does the saved item
     attach to their account or get lost?
   - Is the saved list shared across devices, or per-device/session?

3. Data lifecycle
   - Do saved items expire after a period of time?
   - Is there a maximum number of items a user can save?
   - Does "clear cart" or "clear all" also remove saved items, or are
     they a separate data store?

4. Concurrency
   - If the user has two tabs open and saves the same item in both,
     what happens?
   - If the item goes out of stock while saved, is it removed
     automatically or shown as unavailable?

5. Interaction with adjacent features
   - Does moving an item back to the cart re-check current price and
     stock, or restore the price at the time it was saved?
   - Do saved items count toward any cart-based promotions
     (e.g., "buy 3 get 1 free")?

Đây là nguyên liệu thô — bạn chưa cần trả lời ngay, chỉ cần có một danh sách câu hỏi cụ thể để mang đi hỏi PM hoặc trả lời bằng domain knowledge của chính bạn.

Step 2: Phơi Bày Giả Định Ngầm Bằng Context Probe

Bước thứ hai nhắm vào một loại lỗ hổng khác: không phải "hành vi nào chưa được định nghĩa" mà là "tiền đề nào đang được ngầm giả định là đúng". Prompt này yêu cầu AI đọc yêu cầu như một người ngoài cuộc hoàn toàn, không có bối cảnh công ty:

You are an outside auditor with zero knowledge of our product, our team,
or our current system architecture. You are reading this requirement for
the first time.

Raw requirement:
"Add a 'save for later' button to the cart. When clicked, the item
moves out of the active cart into a saved list the user can access later."

Task: List every implicit assumption this requirement makes that is NOT
stated explicitly. Focus on assumptions about:
- The platform(s) this applies to (web, mobile app, mobile web)
- The current data model (what you'd have to assume about how carts
  are stored today)
- Business rules the author considers "obvious" and didn't write down
- Scope boundaries (what's assumed to be OUT of scope, even though
  it's not said)

Phrase each item as "This requirement assumes that..." so I can confirm
or correct it.

Output mẫu:

- This requirement assumes cart and saved-items data can be modeled as
  two related but separable stores (not guaranteed if today's cart is
  one flat object).
- This requirement assumes this applies to all platforms (web, iOS,
  Android) with identical behavior, since no platform is specified.
- This requirement assumes "later" has no fixed time boundary — items
  are assumed to persist indefinitely unless stated otherwise.
- This requirement assumes the button only affects single items, not
  bundles or multi-item kits currently in the cart.
- This requirement assumes analytics/reporting is out of scope — no
  mention of tracking save/restore events, even though product
  decisions often depend on this data later.
- This requirement assumes pricing shown in the saved list should
  update live, since "save for later" implies a return visit where
  price may have changed — but this is never stated.

Kết hợp Step 1 và Step 2, bạn có hai danh sách bổ trợ nhau: một danh sách câu hỏi hành vi cụ thể (edge case) và một danh sách tiền đề cần xác nhận (assumption). Đây chính là nguyên liệu đầu vào cho phần "Hands-On" bên dưới, nơi bạn sẽ thấy toàn bộ quy trình áp dụng vào một yêu cầu checkout thực tế.

Mẹo: Luôn tách prompt "liệt kê câu hỏi" ra khỏi prompt "đề xuất giải pháp". Nếu gộp chung, AI có xu hướng nhảy thẳng vào implementation detail và bỏ qua việc đặt câu hỏi nền tảng — bạn sẽ mất chính giá trị mà kỹ thuật interrogation mang lại.

Viết Spec Vừa Qua Được PM Review, Vừa Giúp Agent Code Đúng

Một spec kỹ thuật tốt phải phục vụ đồng thời hai đối tượng đọc rất khác nhau: PM — người cần xác nhận spec đúng ý định nghiệp vụ nhưng không đọc được pseudo-code hay database schema, và AI agent (hoặc engineer khác) — người cần đủ chi tiết kỹ thuật để triển khai chính xác mà không phải đoán. Viết một tài liệu chung cho cả hai, thay vì hai tài liệu riêng, giữ cho spec luôn là "nguồn sự thật" duy nhất (single source of truth) — tránh tình trạng PRD nói một đằng, spec kỹ thuật nói một nẻo, và không ai biết bản nào đúng.

Cấu trúc thực tế hiệu quả: mở đầu bằng phần Plain-Language Summary — 2-4 câu mô tả tính năng theo ngôn ngữ nghiệp vụ, không có thuật ngữ kỹ thuật, để PM đọc lướt là hiểu và duyệt được. Ngay sau đó là phần Scope & Non-Goals — nói rõ cái gì làm, cái gì cố tình không làm trong lần này, vì đây là chỗ PM hay bất ngờ nhất khi thấy sản phẩm launch ("ủa sao không có X?"). Việc ghi non-goals tường minh cũng bảo vệ engineer: nó biến "chưa làm" từ một thiếu sót thành một quyết định có chủ đích, đã được PM duyệt.

Phần thân của spec nên tổ chức theo khối:

  • Behavior specification: mô tả hành vi hệ thống dưới dạng Given/When/Then hoặc bảng input → expected output, đủ cụ thể để không còn khoảng trống diễn giải. Đây là phần chuyển hóa trực tiếp từ danh sách câu hỏi bạn đã trả lời ở bước interrogation.
  • Data model & state changes: field nào được thêm/sửa, trạng thái nào tồn tại, transition nào hợp lệ. Đây là phần agent cần đọc kỹ nhất.
  • Acceptance criteria: danh sách điều kiện đo lường được, đánh số, mỗi điều kiện độc lập và test được riêng lẻ.
  • Explicitly out of scope: liệt kê lại các case đã được xác nhận là "không xử lý ở version này" — tránh agent tự ý implement thêm ngoài phạm vi.
  • Open questions / risks: những điều chưa chốt được, cần review thêm trước khi code.

Nguyên tắc "PM review được" không có nghĩa là loại bỏ chi tiết kỹ thuật — nó có nghĩa là phân lớp (layer) thông tin: người đọc lướt chỉ cần đọc Plain-Language Summary và Scope để duyệt; người cần chi tiết (agent, engineer) đọc tiếp phần Behavior và Data model. Một sai lầm phổ biến là viết spec quá kỹ thuật ngay từ dòng đầu — PM bỏ qua không đọc, chỉ gật đầu theo phản xạ, và bạn mất đi lớp kiểm tra thực sự quan trọng: xác nhận rằng bạn hiểu đúng ý định nghiệp vụ trước khi tốn công triển khai.

Với agent, mức độ cụ thể quan trọng hơn độ dài. Một acceptance criterion như "Discount should not exceed 10%" mơ hồ ở chỗ 10% của cái gì — order subtotal trước thuế, sau thuế, hay bao gồm phí ship? Viết lại thành "Discount amount must not exceed 10% of the order subtotal, calculated before tax and shipping fees are applied" loại bỏ hoàn toàn khoảng trống diễn giải mà một agent có thể lấp bằng giả định sai.

Mẹo: Sau khi viết xong spec, tự hỏi: "Nếu tôi giao spec này cho một agent chưa từng thấy codebase, nó có thể viết code đúng mà không cần hỏi lại tôi câu nào không?" Nếu câu trả lời là "còn vài chỗ nó phải đoán", đó là chỗ cần cụ thể hóa thêm trước khi chạy agent.

Thực Hành: Từ Yêu Cầu Checkout Đến Spec Kỹ Thuật Hoàn Chỉnh

Phần này đi qua toàn bộ quy trình, từ yêu cầu thô đến spec sẵn sàng đưa cho agent, với một yêu cầu checkout thực tế:

Raw requirement: "Add support for applying a single discount code at checkout, with a 10% max discount cap per order."

Step 1: Chạy Prompt Thẩm Vấn Edge Case

Chạy prompt sau để lấy danh sách câu hỏi cần trả lời:

You are a senior engineer reviewing a raw product requirement before it
becomes an engineering spec. Do NOT propose a solution or write any code.

Raw requirement:
"Add support for applying a single discount code at checkout, with a
10% max discount cap per order."

Task: Enumerate edge cases this requirement does not address, grouped by:
1. Discount code validity and eligibility
2. Cap calculation (what the 10% applies to)
3. Interaction with other pricing mechanisms (existing sales, shipping,
   tax, gift cards)
4. Error handling and user feedback
5. Order lifecycle (edits, cancellations, refunds after a discount
   was applied)

For each edge case, phrase it as a specific question I need to answer.
List 3-5 questions per group.

Step 2: Trả Lời Câu Hỏi Bằng Kiến Thức PM/Domain Của Bạn

AI trả về khoảng 20 câu hỏi. Dưới đây là các câu quan trọng nhất và câu trả lời thực tế (giả định bạn đã hỏi PM / tra cứu domain knowledge):

  • "Single discount code" nghĩa là gì nếu order đã có sẵn một mã khác? → Trả lời: chỉ một mã được active tại một thời điểm; nhập mã mới sẽ thay thế mã cũ, không cộng dồn.
  • 10% áp dụng trên số tiền nào? → Trả lời: trên order subtotal, tính trước thuế và phí ship, sau khi đã trừ mọi discount khác đã áp dụng sẵn (ví dụ sale price của sản phẩm).
  • Nếu discount code trong DB có giá trị cao hơn 10% (ví dụ code giảm 20%), hệ thống làm gì? → Trả lời: vẫn áp dụng code, nhưng số tiền giảm thực tế bị cap ở 10% subtotal; UI hiển thị rõ "capped at 10%" để user không thấy khó hiểu.
  • Discount code có áp dụng được cho guest checkout không? → Trả lời: có, discount code không gắn với account.
  • Điều gì xảy ra khi user sửa giỏ hàng (thêm/bớt item) sau khi đã áp mã? → Trả lời: subtotal thay đổi, hệ thống phải tính lại 10% cap real-time trước khi cho phép submit order.
  • Nếu order bị hủy một phần (partial refund), discount có được tính lại tỷ lệ theo phần còn lại không? → Trả lời: có, refund phải trừ theo tỷ lệ discount đã áp dụng cho item bị hoàn, không giữ nguyên số tiền giảm gốc.
  • Mã hết hạn/không hợp lệ thì thông báo lỗi hiển thị thế nào? → Trả lời: hiển thị inline error ngay dưới ô nhập mã, không chặn luồng checkout, cho phép user tiếp tục không cần mã.

Step 3: Sinh Bản Draft Spec Từ Các Câu Trả Lời

Đưa toàn bộ câu hỏi + câu trả lời vào prompt sinh draft spec:

You are a senior engineer writing an engineering spec from a requirement
that has already been clarified through a Q&A pass. Do not introduce
new business rules beyond what is stated below.

Requirement: "Add support for applying a single discount code at
checkout, with a 10% max discount cap per order."

Clarifications (question -> answer):
1. Only one discount code can be active per order; entering a new code
   replaces the previous one.
2. The 10% cap applies to the order subtotal, calculated before tax and
   shipping, and after any other discounts (e.g. sale prices) are
   already deducted.
3. If a code's face-value discount exceeds 10% of subtotal, the applied
   amount is capped at 10%, and the UI must show a "capped" message.
4. Discount codes work for guest checkout; not tied to account.
5. If cart contents change after a code is applied, the 10% cap is
   recalculated against the new subtotal before order submission.
6. Partial refunds reduce the discount amount proportionally to the
   refunded items' share of the original subtotal.
7. Invalid/expired codes show an inline error under the code input
   field and do not block checkout without a code.

Task: Produce a draft engineering spec with these sections:
- Plain-Language Summary
- Scope & Non-Goals
- Behavior Specification (Given/When/Then)
- Data Model & State Changes
- Acceptance Criteria (numbered)
- Explicitly Out of Scope
- Open Questions

Keep the Plain-Language Summary free of technical jargon.

Đoạn trích mẫu từ draft spec sinh ra:

## Plain-Language Summary
Customers can enter one discount code at checkout. The system will
apply the discount, but the total discount will never exceed 10% of
the order's subtotal, even if the code itself offers more.

## Behavior Specification
- Given a cart subtotal of $200 and a code worth 15% off,
  When the customer applies the code,
  Then the discount applied is capped at $20 (10% of subtotal), and
  the UI shows "Discount capped at 10% of order value."

- Given a discount code is already applied,
  When the customer enters a second code,
  Then the first code is removed and only the second code's discount
  is evaluated against the cap.

- Given a discount is applied and the customer removes an item from
  the cart,
  When the new subtotal is calculated,
  Then the discount amount is recalculated against the new subtotal
  before the customer can submit the order.

## Acceptance Criteria
1. A customer can apply exactly one discount code per order; applying
   a second code replaces the first.
2. The applied discount amount must never exceed 10% of the order
   subtotal, calculated before tax and shipping and after other
   discounts.
3. When a code's value exceeds the 10% cap, the UI displays a message
   indicating the discount was capped.
4. Guest checkout sessions can apply discount codes without an account.
5. Editing cart contents after applying a code triggers recalculation
   of the cap before order submission is allowed.
6. Partial refunds reduce the discount amount in proportion to the
   refunded items' share of the original subtotal.

Step 4: Chuyển Acceptance Criteria Thành Agent Task

Mỗi acceptance criterion trở thành một task cụ thể cho agent, kèm đủ ngữ cảnh để agent không phải tra ngược lại spec:

Implement acceptance criterion #2 from checkout-discount-spec.md:

"The applied discount amount must never exceed 10% of the order
subtotal, calculated before tax and shipping and after other discounts."

Context:
- Subtotal is computed in `calculateOrderSubtotal()` in
  src/checkout/pricing.ts, already excludes tax and shipping, and
  already reflects any per-item sale prices.
- Discount codes are validated in `applyDiscountCode()` in
  src/checkout/discounts.ts.

Task:
1. In `applyDiscountCode()`, after computing the code's raw discount
   value, clamp it to a maximum of 10% of the subtotal returned by
   `calculateOrderSubtotal()`.
2. Return a flag `wasCapped: boolean` alongside the discount amount so
   the UI layer can show the "capped" message.
3. Add unit tests covering: discount under cap, discount exactly at
   cap, discount over cap, and subtotal of zero.

Do not modify tax or shipping calculation logic.

Cách viết này giữ agent trong đúng phạm vi của một acceptance criterion, tránh tình trạng giao cả spec dài cho agent và để nó tự quyết định thứ tự/độ ưu tiên triển khai.

Step 5: Kiểm Tra Mâu Thuẫn Trong Spec Trước Khi Implement

Trước khi giao cho agent, chạy một pass riêng để kiểm tra chính spec bạn vừa viết có tự mâu thuẫn không:

You are reviewing an engineering spec for internal consistency before
implementation begins. Do not suggest new features.

Spec (paste full content of checkout-discount-spec.md here):
[...]

Task: Identify any internal contradictions or gaps between sections,
specifically:
- Does the Behavior Specification contradict any Acceptance Criterion?
- Does any Acceptance Criterion assume a data field or state that is
  not defined in Data Model & State Changes?
- Are there two acceptance criteria that could both be "true" but
  produce conflicting system behavior in the same scenario?
- Does "Explicitly Out of Scope" contradict anything implied by the
  Behavior Specification?

List each conflict found with the exact section/line it references.

Trong ví dụ này, một lần chạy thực tế phát hiện: acceptance criterion #6 (refund tỷ lệ theo phần trả lại) giả định hệ thống lưu lại "discount amount tại thời điểm order được đặt", nhưng phần Data Model trong draft ban đầu chưa định nghĩa field nào lưu giá trị đó — nó chỉ lưu discount code, không lưu số tiền đã áp dụng tại thời điểm order. Đây chính xác là loại lỗ hổng mà bước conflict check này được thiết kế để bắt trước khi agent viết code dựa trên một data model thiếu field.

Step 6: Chốt Và Lưu Trữ Spec Trước Khi Chạy Agent

Sau khi vá lỗ hổng phát hiện ở Step 5 (thêm field appliedDiscountAmount vào order record), chốt spec và lưu trữ đúng chỗ:

  • Lưu file spec trong repo, cạnh code liên quan — ví dụ docs/specs/checkout-discount-cap.md — để spec luôn version cùng với code, không lạc mất trong một tài liệu Google Docs mà không ai cập nhật.
  • Link spec từ chính ticket Jira/Linear (paste đường dẫn file trong repo, không copy nội dung sang ticket) để PM và engineer luôn tham chiếu cùng một nguồn duy nhất.
  • Đánh dấu trạng thái phê duyệt ngay trong file, ví dụ dòng đầu spec: Status: Approved by PM — 2026-07-08, để mọi người biết đây không còn là draft.
  • Chỉ sau khi spec ở trạng thái "Approved", mới đưa từng acceptance criterion vào task cho agent như ở Step 4. Nếu chạy agent trên spec còn "Draft", bạn có nguy cơ phải throw away code khi PM yêu cầu đổi.

Mẹo: Đặt tên file spec theo pattern nhất quán (docs/specs/{feature-slug}.md) và bắt buộc PR liên quan phải link tới file này trong mô tả PR — việc này biến spec thành tài liệu sống, được review lại mỗi khi code đổi, thay vì một tài liệu chết chỉ đọc một lần.

Những điểm chính

  • Yêu cầu nghiệp vụ đi qua một chuỗi dịch thuật nhiều bước (business ask → PRD → spec → acceptance criteria → agent task); engineer phải chủ động sở hữu bước PRD-sang-spec, không được để AI agent tự lấp khoảng trống.
  • Ba loại lỗ hổng phổ biến nhất — ambiguity, contradiction, assumed context — thường không lộ ra cho đến khi bạn thử viết acceptance criteria hoặc liệt kê edge case cụ thể.
  • Dùng AI như một Socratic interrogator: tách riêng prompt liệt kê edge case (Step 1) và prompt phơi bày giả định ngầm (Step 2), luôn yêu cầu AI đặt câu hỏi thay vì tự đề xuất giải pháp.
  • Spec tốt phục vụ hai đối tượng cùng lúc: Plain-Language Summary cho PM review nhanh, và Behavior Specification / Data Model chi tiết đủ để agent triển khai không cần đoán.
  • Trước khi giao spec cho agent, luôn chạy một pass conflict check riêng để phát hiện mâu thuẫn nội tại — lỗi phát hiện ở giai đoạn này rẻ hơn nhiều so với lỗi phát hiện sau khi code đã chạy.
  • Lưu spec đã duyệt trong repo, link từ ticket, đánh dấu trạng thái approved rõ ràng — biến spec thành nguồn sự thật sống, không phải tài liệu một lần dùng rồi bỏ.