Tìm hiểu cách viết ràng buộc và non-goals trong đặc tả ứng dụng để giảm việc làm lại. Dùng định dạng đơn giản cho stack, ngân sách, hạn chót cố định và những gì có thể thay đổi.

Làm lại xảy ra khi bạn xây được thứ hoạt động, nhưng đó không phải là thứ phù hợp cho dự án. Nhóm phải làm lại màn hình, viết lại logic, di chuyển dữ liệu, hoặc xây lại một tính năng vì một quyết định quan trọng xuất hiện muộn.
Nó thường xuất hiện theo những cách quen thuộc: một luồng bị làm lại vì giả định sai về vai trò người dùng; màn hình phải thiết kế lại vì hỗ trợ mobile được mong đợi nhưng chưa được nêu; mô hình dữ liệu thay đổi vì “cần lịch sử audit” xuất hiện sau phiên bản một; một tích hợp bị đổi vì khách hàng không thể dùng dịch vụ bên thứ ba; hoặc ứng dụng phải chuyển hosting vì luật tuân thủ hoặc yêu cầu vùng.
Thiếu ràng buộc tạo ra những quyết định bất ngờ về sau. Khi một đặc tả viết “xây CRM,” nó để lại hàng chục câu hỏi mở: ai dùng nó, nền tảng nào quan trọng, quy tắc bảo mật nào áp dụng, điều gì phải nằm ngoài phạm vi, ngân sách và timeline thật là bao nhiêu. Nếu câu trả lời tới sau khi đã có mã, dự án trả giá hai lần: một lần để xây, một lần để hủy.
Một ví dụ đơn giản: một nhà sáng lập yêu cầu “đặt lịch + nhắc nhở.” Tuần đầu giao email reminders. Tuần hai họ nói cần SMS, nhưng SMS không được phép ở quốc gia họ hoặc vượt ngân sách. Lúc này hệ thống nhắc nhở phải thiết kế lại, màn hình thay đổi, và kiểm thử bắt đầu lại. Việc làm lại không phải do code tệ. Là do ràng buộc xuất hiện muộn.
Mục tiêu là giảm lặp đi lặp lại trước khi có bất kỳ mã nào được viết hoặc sinh ra. Dù bạn code tay hay dùng công cụ dựa trên chat, đầu ra chỉ theo các quy tắc bạn đặt. Nếu quy tắc tới muộn, công việc sẽ chuyển hướng và bạn phải làm lại.
Đây không phải là viết một tài liệu dài. Một đặc tả nhẹ vẫn có thể nghiêm ngặt ở chỗ quan trọng. Ban đầu, nó nên trả lời:
Khi ràng buộc và non-goals được ghi trước, chúng đóng vai trò như lan can bảo vệ. Bạn nhận ít bất ngờ hơn, ít phải xây lại, và quyết định rõ ràng hơn từ ngày đầu.
Ràng buộc là quyết định cố định mà dự án phải sống chung. Bỏ qua chúng và bạn làm việc hai lần, vì bạn xây theo hướng không thể phát hành.
Non-goals là lựa chọn rõ ràng không làm một thứ gì đó. Bỏ qua chúng và đặc tả âm thầm phình ra khi mọi người thêm các “tiện nhỏ”. Đó là cách bạn kết thúc bằng việc phải làm lại màn hình, luồng, và mô hình dữ liệu.
Một quy tắc nhanh: ràng buộc giới hạn cách bạn xây; non-goals giới hạn thứ bạn xây.
Ràng buộc là một điều bắt buộc và không thay đổi nếu không có một quyết định thực sự (và một đánh đổi).
Ví dụ:
Khi một ràng buộc là thật, viết nó như một câu bạn không thể tranh cãi. Nếu ai đó nói “có thể,” thì nó chưa là ràng buộc.
Non-goal là một câu “chúng tôi không làm điều này,” ngay cả khi nghe có vẻ hữu ích. Nó bảo vệ phát hành đầu.
Ví dụ:
Non-goals không phải tiêu cực. Chúng ngăn những đường vòng tốn kém. Ví dụ, “không roles tuỳ chỉnh trong v1” có thể cứu bạn khỏi vài tuần xử lý các trường hợp ngoại lệ về quyền khiến phải thiết kế lại cơ sở dữ liệu và UI.
Trước khi viết hàng trang chi tiết, hãy viết một câu ghim dự án vào tường. Nó giữ mọi người cùng hướng khi phải đánh đổi.
Một câu tóm tắt tốt trả lời: dành cho ai, và công việc chính nó làm là gì?
Ví dụ câu tóm tắt:
Sau đó thêm định nghĩa thành công ngắn: 3–5 kết quả mà người dùng thực sự phải đạt khi dự án xong. Viết chúng như kết quả người dùng, không phải tính năng.
Với ví dụ đặt lịch cho gia sư:
Nếu bạn chưa có số liệu, mô tả thành công bằng lời. “Nhanh” thì mơ hồ, nhưng “cảm thấy nhanh trên điện thoại” vẫn hữu ích. “Dễ” mơ hồ, nhưng “không cần cuộc gọi hướng dẫn” thì rõ hơn. Bạn có thể thêm số sau.
Giữ phần này ngắn. Nó sẽ là bối cảnh cho mọi thứ sau: điều gì phải đúng, điều gì không được xảy ra, và điều gì có thể thay đổi.
Làm lại thường bắt đầu khi lịch trình và quy trình quyết định chỉ sống trong đầu ai đó. Đặt các ràng buộc dự án vào đặc tả trước khi mô tả màn hình và tính năng.
Viết chúng như các câu rõ ràng, có thể kiểm tra:
Ví dụ đơn giản:
“Phát hành đầu phải xong trước ngày 30 tháng 5. Bao gồm login, danh sách khách hàng cơ bản, và một báo cáo hàng tháng. Không tích hợp trong v1. Ngân sách giới hạn $8,000 bao gồm hosting cho tháng đầu. Reviews trong vòng 24 giờ vào ngày trong tuần. Product owner là Sam, người phê duyệt thay đổi phạm vi.”
Tốc độ phản hồi đáng được ghi riêng vì nó điều khiển mức an toàn khi bạn tiến. Nếu các bên liên quan chỉ review một lần mỗi tuần, đặc tả nên ưu tiên phát hành nhỏ hơn và ít trường hợp cạnh.
Chọn chu kỳ review phù hợp với thực tế: phản hồi trong ngày, 24–48 giờ trong ngày làm việc, họp review hàng tuần, hoặc (hiếm) “không cần phản hồi.”
Nếu bạn không nêu ràng buộc kỹ thuật sớm, mọi người sẽ tự lấp đầy khoảng trống bằng giả định. Đó là cách các nhóm phải làm lại màn hình, migration, hoặc tích hợp sau khi công việc đã bắt đầu.
Bắt đầu bằng việc nói rõ phần nào bị khoá và phần nào chỉ là ưu tiên. “Ưu tiên React” không giống “phải là React vì chúng tôi dựa vào thư viện component nội bộ.” Một câu cho mỗi quyết định là đủ.
Hãy cụ thể cho toàn bộ app: web, backend, database, mobile. Nếu một phần linh hoạt, nói rõ và đặt giới hạn (ví dụ “mobile là web-only trong v1”).
Cách viết đơn giản:
Rồi liệt kê các tích hợp không thể tránh. Nêu tên hệ thống (payments, email, analytics, CRM) và giới hạn cứng. Ví dụ: “Phải dùng Stripe cho thanh toán,” “Gửi email qua nhà cung cấp hiện có,” “Analytics không được theo dõi dữ liệu cá nhân.” Nếu authentication cố định (SSO, Google login, passwordless), ghi rõ.
Lựa chọn hosting thay đổi kiến trúc. Ghi rõ nơi app phải chạy và lý do: “Phải chạy ở Đức,” “Dữ liệu phải ở EU,” hoặc “Có thể chạy toàn cầu.”
Nếu có nhu cầu tuân thủ, giữ chúng cụ thể: thời gian lưu, quy tắc xóa, và nhu cầu audit.
Ví dụ: “Lưu hồ sơ 7 năm, xóa trong vòng 30 ngày kể từ yêu cầu đã xác minh, lưu log ai xem hồ sơ, và deploy chỉ ở quốc gia nơi bệnh nhân sinh sống.” Những dòng này tránh các bất ngờ muộn ngay khi bạn chuẩn bị phát hành.
Non-goals là lan can của đặc tả. Chúng nói bạn không xây gì, không hỗ trợ gì, hoặc không cố gắng hoàn thiện điều gì trong phát hành đầu. Đây là cách nhanh nhất để giảm bất ngờ, vì nhiều yêu cầu “nhỏ” tới sau và âm thầm thay đổi toàn bộ kế hoạch.
Một non-goal tốt đủ cụ thể để đồng đội phát hiện scope creep chỉ bằng một câu. Nó cũng nên có giới hạn thời gian. “Không trong v1” rõ ràng hơn “chúng tôi sẽ không làm điều này.”
Bắt đầu với các tính năng mà người ta thường giả định có. Với app đặt lịch đơn giản, có thể là:
Những thứ này không phải là xấu; chúng tốn kém. Viết chúng ra giữ phát hành đầu tập trung.
Cũng nêu các mục “chi tiết” gây domino lớn: roles, permissions, và luồng xử lý ngoại lệ. “Không roles tuỳ chỉnh. Chỉ hai roles: Owner và Member.” Một dòng như vậy có thể cứu vài tuần.
Nhóm thường quên non-goals không phải tính năng. Chúng xuất hiện sau này dưới dạng làm lại đau đầu.
Quyết định điều bạn không tối ưu. Ví dụ: “Chúng tôi không tối ưu cho 1M users. Giả định tối đa 500 weekly active users trong v1.”
Cũng ghi những gì không hỗ trợ để kiểm thử thực tế: “Không Internet Explorer,” “Không layout riêng cho tablet,” hoặc “Đăng nhập chỉ bằng email & mật khẩu (không SSO, không magic links).”
Đặc tả an toàn khi cho phép vài quyết định nhỏ tiến hoá. Nếu bạn chỉ viết những gì cố định, mọi ý tưởng mới đều trở thành tranh luận. Một danh sách “có thể thay đổi” ngắn cho phép mọi người cải thiện sản phẩm mà không khởi động lại toàn bộ kế hoạch.
Giữ thực tế. Bao phủ những gì bạn mong học sau khi có phiên bản chạy, không phải tính năng lớn mới. Các mục thường linh hoạt: văn bản UI, tinh chỉnh luồng nhỏ, cột báo cáo, đặt tên (roles, statuses, categories), và lựa chọn bố cục cơ bản.
Tiếp theo, quyết định cách chấp nhận thay đổi. Thiếu quy tắc duyệt đơn giản, “tinh chỉnh nhanh” biến thành scope creep lặng lẽ.
Một quy trình đơn giản phù hợp cho đội nhỏ:
Quy tắc chính: thay đổi linh hoạt không được phá vỡ các ràng buộc cố định. Nếu stack là React + Go + PostgreSQL, một yêu cầu “có thể thay đổi” không thể thành “chúng ta đổi backend.” Nếu hạn chót cố định, “có thể thay đổi” không có nghĩa là thêm module mới cần hai tuần nữa.
Thêm một ghi chú đánh đổi mà mọi người đồng ý. Ví dụ: “Nếu thêm role mới với quyền tuỳ chỉnh, ta trì hoãn báo cáo nâng cao sang pha 2.”
Một đặc tả tốt bắt đầu bằng việc giới hạn lựa chọn, không mở rộng. Định dạng này ép bạn viết quy tắc trước khi ai đó bắt đầu xây.
Dùng phần này làm header trong tài liệu của bạn:
SPEC v0.1 (date)
Owner:
Reviewers:
1) One-liner
- Build: [what it is]
- For: [who]
- So they can: [main benefit]
2) Success definition (3 outcomes)
- Outcome 1: [measurable result]
- Outcome 2: [measurable result]
- Outcome 3: [measurable result]
3) Fixed constraints (cannot change without re-approval)
- Deadline: [date]
- Budget: [$ or hours]
- People: [who is available]
- Tech stack: [fixed choices]
- Hosting/region: [where it must run]
4) Non-goals (must NOT happen)
- [explicit “no”]
- [explicit “not in v1”]
- [explicit “we won’t support”]
5) Open questions
- Q: [question]
Owner: [name]
Due: [date]
6) Lock rule
- After review: changes require: [what approval looks like]
Hầu hết bất ngờ không phải do vận rủi. Chúng xảy ra vì đặc tả để lại khoảng cho các cách hiểu khác nhau.
Một bẫy thường gặp là trộn mục tiêu và giải pháp. Nhóm nhảy thẳng vào màn hình và luồng trước khi ghi rõ điều gì cố định (hạn chót, ngân sách, stack) và điều gì ngoài phạm vi. Kết quả là một kế hoạch UI đẹp nhưng không vừa với ràng buộc.
Bẫy khác là non-goals mơ hồ. “Không tính năng thêm” nghe có vẻ nghiêm, nhưng không bảo vệ bạn khi ai đó yêu cầu “thêm một báo cáo nhỏ” hoặc “một admin panel nhanh”. Non-goals tốt là cụ thể và có thể kiểm tra.
Ngân sách ẩn hoặc hạn chót “mềm” cũng là bom nổ chậm. Nếu ngân sách thật là $5k mà đặc tả đọc như sản phẩm $50k, đội sẽ xây sai. Đặt những con số khó chịu lên giấy.
Tích hợp và quyền sở hữu dữ liệu cũng gây bất ngờ lặng lẽ. Nếu bạn nói “kết nối Stripe” nhưng không định nghĩa event nào, trường nào, và ai sở hữu dữ liệu, bạn sẽ lặp lại cùng quyết định nhiều lần.
Bẫy cuối là thay đổi ràng buộc giữa chừng mà không nêu rõ đánh đổi. Chuyển từ “chỉ web” sang “web + mobile”, hoặc từ “dùng Postgres” sang “dùng gì rẻ nhất” làm thay đổi cả kế hoạch. Có thể thay đổi, nhưng phải cập nhật phạm vi, timeline hoặc mong đợi chất lượng.
Thêm một ghi chú ngắn trong đặc tả trả lời 5 điểm:
Trước khi ai đó bắt đầu xây, bạn nên trả lời được các câu “cái gì cố định?” mà không phải lục tài liệu dài.
Kiểm tra nhanh:
Nếu thiếu một trong số này, bản build đầu vẫn diễn ra, nhưng bản build thứ hai mới là bản thật.
Các bước tiếp theo giữ đà mà không khoá bạn vào quyết định xấu:
Nếu bạn dùng Koder.ai (koder.ai), “Planning Mode” cùng một phần ràng buộc và non-goals rõ ràng giúp nền tảng sinh bản nháp đầu phù hợp với stack, vùng hosting và phạm vi. Và nếu ưu tiên thay đổi, tính năng như snapshots và rollback cho phép thử thay đổi mà không mất baseline ổn định. Xuất mã nguồn hữu ích nếu cần chuyển công việc đi nơi khác sau này.
Khi những quy tắc này được viết sớm, các cuộc thảo luận về tính năng dễ dàng hơn vì mọi người biết điều gì phải cố định và điều gì có thể dịch chuyển.
Rework là khi bạn xây dựng một thứ hoạt động, nhưng không thể đưa vào phát hành vì một quyết định muộn thay đổi quy tắc. Nó thường xảy ra khi đặc tả không nêu rõ các ràng buộc chính từ đầu, nên nhóm đưa ra giả định hợp lý nhưng sau đó mới phát hiện là sai.
Bắt đầu với những thứ không thể thay đổi nếu không có một đánh đổi thực sự, như hạn chót, giới hạn ngân sách, khu vực lưu trữ, stack bắt buộc, và yêu cầu tuân thủ. Sau đó thêm một phần ngắn về non-goals để mọi người không âm thầm mở rộng phạm vi bằng những tính năng “nhỏ”.
Ràng buộc (constraint) giới hạn cách bạn xây dựng, ví dụ “phải chạy trong EU” hoặc “phải dùng React và PostgreSQL”. Non-goal giới hạn thứ bạn xây, ví dụ “không làm mobile trong v1” hoặc “không có custom roles khi ra mắt”.
Viết nó như một câu có thể kiểm tra được, không phải là một sở thích. Nếu ai đó có thể nói “có thể” và không ai bắt buộc tuân theo, thì đó chưa phải là ràng buộc thực sự và nên coi là câu hỏi còn mở.
Chọn 3–5 kết quả người dùng mô tả thành công cho phát hành đầu tiên, bằng ngôn ngữ đơn giản. Kết quả giúp đội tập trung vào điều người dùng cần làm hơn là liệt kê tính năng. Ví dụ: “Người dạy có thể đặt lịch trong vài phút”, “Học viên đặt buổi mà không cần gọi điện”.
Những ràng buộc thường bị ẩn gồm: hỗ trợ mobile, roles & permissions, lịch sử kiểm tra (audit history), nơi lưu trữ dữ liệu, và các tích hợp mà khách hàng không thể dùng. Nếu nêu sớm những thứ này, bạn tránh phải thiết kế lại màn hình, thay đổi mô hình dữ liệu hoặc đổi nhà cung cấp muộn.
Hãy cụ thể và có giới hạn thời gian, như “không trong v1” hoặc “chúng tôi sẽ không hỗ trợ tablet”. Một non-goal mơ hồ như “không thêm tính năng” sẽ không ngăn scope creep vì nó không chặn được các yêu cầu cụ thể.
Ghi rõ ai phê duyệt thay đổi, tốc độ phản hồi, và chu kỳ đánh giá bạn sẽ dùng. Phản hồi chậm là một ràng buộc thực sự vì nó ảnh hưởng tới tốc độ lặp và mức độ bất định bạn có thể chấp nhận.
Liệt kê chúng như câu hỏi mở với một người chịu trách nhiệm và ngày hoàn thành, và đừng bắt đầu xây phần liên quan cho tới khi câu trả lời được khoá. Nếu phải bắt đầu, hãy nêu rõ giả định bạn đang dùng để sau này dễ xem xét lại mà không gây nhầm lẫn.
Dùng quy trình lập kế hoạch để khoá constraints và non-goals trước khi sinh ra bất cứ thứ gì, để bản nháp đầu tiên phù hợp với stack, vùng lưu trữ và phạm vi. Nếu ưu tiên thay đổi, các tính năng như snapshots và rollback giúp thử nghiệm mà không mất baseline ổn định, và xuất mã nguồn giúp chuyển tiếp công việc nếu cần.