Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Bài 52 — Versioning trong Spec (v1 / v2 / future)

Viết PRD & Product Spec cho PM Bài 52/60

Mở đầu — vì sao bài này quan trọng

Hãy tưởng tượng tình huống này: bạn là PM của một ứng dụng fintech ở TP.HCM. Bạn viết PRD cho tính năng "rút tiền nhanh", gửi cho team. Engineer đọc bản nháp đầu tiên, ước lượng 3 sprint. Hai tuần sau, sau buổi review với Legal, bạn sửa lại spec — thêm bước xác thực OTP, bỏ giới hạn số tiền tối thiểu, đổi luồng thông báo. Bạn ấn "Save". Tài liệu trên Confluence vẫn chỉ có một đường link duy nhất.

Đến ngày sprint planning, designer mở tài liệu và thiết kế theo luồng OTP. Còn anh backend engineer — người đã đọc bản cũ từ hai tuần trước và lưu nó trong đầu — vẫn code theo luồng không OTP. Đến khi QA test, hai bên cãi nhau: "Spec đâu có ghi OTP?" — "Có chứ, ngay đây này." Cả hai đều đúng, vì cả hai đang nói về hai phiên bản khác nhau của cùng một tài liệu, mà không ai gọi tên được phiên bản nào.

Đây là lý do versioning trong spec không phải là chuyện hình thức hay "cho đẹp". Một PRD là living document — tài liệu sống, thay đổi liên tục từ lúc nháp đến lúc ship và thậm chí sau khi ship. Khi nhiều người cùng tham chiếu đến nó qua thời gian, mỗi người cần biết chính xác họ đang nói về phiên bản nào, cái gì đã đổi, và vì sao nó đổi. Bài này dạy bạn cách đánh version cho spec một cách có kỷ luật, để tài liệu của bạn không bao giờ trở thành nguồn gây hiểu lầm — mà ngược lại, trở thành nguồn sự thật rõ ràng cho cả team.

Khái niệm cốt lõi

Vì sao phải có version

Có ba lý do nền tảng khiến versioning trở nên bắt buộc khi spec của bạn vượt qua quy mô "một người tự đọc":

Thứ nhất, PRD là tài liệu sống. Khác với một bản hợp đồng ký xong là đóng băng, PRD thay đổi suốt vòng đời sản phẩm. Nó bắt đầu là vài dòng ý tưởng, lớn dần qua các vòng phản hồi, được điều chỉnh khi gặp ràng buộc kỹ thuật, rồi tiếp tục cập nhật cả sau khi tính năng đã lên production. Một tài liệu thay đổi mà không có dấu mốc thì cũng giống như một con đường không có cột km — bạn không biết mình đang ở đâu trên hành trình đó.

Thứ hai, engineer và designer tham chiếu đến phiên bản cụ thể. Khi một engineer ghi trong commit message "implement theo PRD v1.2 mục 3.4", họ đang neo công việc của mình vào một trạng thái xác định của tài liệu. Nếu sau này tài liệu đổi nhưng version không đổi, sự neo đó vô nghĩa. Version cho phép mọi người trong team nói cùng một ngôn ngữ về trạng thái của tài liệu, không chỉ về nội dung của nó.

Thứ ba, lịch sử quyết định rất quan trọng (decision history matters). Sáu tháng sau, một PM mới vào team mở PRD và thắc mắc "tại sao chúng ta không làm tính năng X?". Nếu version history ghi lại rằng "ở v1.3, X bị chuyển sang Non-goal sau khi Data team chỉ ra rằng nó chỉ ảnh hưởng 0.8% người dùng", thì câu trả lời nằm sẵn ở đó. Không có lịch sử version, mọi quyết định đã chôn vùi đều phải đào lại từ đầu — thường là bằng cách hỏi những người có thể đã rời công ty.

Versioning scheme — chọn cách đánh số

Có nhiều cách đánh version, và bạn nên chọn một cách đủ đơn giản để cả team tuân theo. Đừng bê nguyên semantic versioning của software (1.4.27) vào tài liệu — nó quá chi tiết cho một PRD.

Cách phổ biến và đáng dùng nhất là dùng hai cấp: major.minor.

  • v0.x — giai đoạn nháp (draft). Mọi thứ trước khi tài liệu được "chốt" để bắt đầu làm đều mang số 0 ở đầu. v0.1 là bản nháp đầu, v0.2, v0.3... là các vòng chỉnh sửa trong lúc còn thảo luận. Số 0 ở đầu là tín hiệu ngầm: "nội dung này chưa ổn định, đừng bắt đầu code dựa trên nó."
  • v1.0 — bản đã chốt (approved / locked for build). Khi tài liệu được các stakeholder đồng ý và team bắt đầu xây dựng, nó lên v1.0. Đây là cột mốc quan trọng nhất. v1.0 nghĩa là "đây là hợp đồng để chúng ta cùng làm v1 của tính năng."
  • Tăng minor (v1.1, v1.2) khi có thay đổi nhỏ trong lúc đang build — làm rõ một acceptance criteria, sửa một con số, thêm một edge case. Nội dung cốt lõi không đổi, chỉ tinh chỉnh.
  • Tăng major (v2.0) khi có thay đổi lớn về phạm vi hoặc hướng đi — ví dụ sau khi ship v1 và quyết định làm một vòng tính năng tiếp theo, hoặc khi hướng giải pháp thay đổi căn bản giữa chừng.

Phân biệt versioning tài liệu với versioning sản phẩm

Đây là chỗ rất nhiều PM nhầm. Có hai loại "version" sống cạnh nhau trong cùng một spec, và bạn phải tách bạch chúng:

  • Version của tài liệu (document version): v0.1 → v1.0 → v1.1... — phản ánh tài liệu đã được sửa bao nhiêu lần.
  • Version của tính năng/sản phẩm (product/feature version): "v1 của tính năng", "v2 của tính năng" — phản ánh các giai đoạn ship khác nhau của chính sản phẩm.
Một PRD v1.4 (đã sửa 4 lần) hoàn toàn có thể đang mô tả "v1 của tính năng" và đồng thời có một mục "Future / v2" liệt kê những gì sẽ làm ở giai đoạn sau. Hai trục này độc lập. Khi viết, hãy luôn nói rõ bạn đang nói về trục nào.

Cấu trúc "v1 / v2 / Future" trong nội dung spec

Ngoài việc đánh số tài liệu, bản thân nội dung spec nên phân tầng theo phạm vi ship. Đây là một trong những kỹ thuật giá trị nhất để giữ scope không phình to:

  • v1 (Now): Những gì sẽ ship trong lần này. Phải đầy đủ chi tiết, có acceptance criteria.
  • v2 (Next): Những gì đã có chủ đích làm nhưng cố tình hoãn. Mô tả ở mức ý tưởng, đủ để team hiểu hướng đi nhưng không cần chi tiết.
  • Future / Someday: Những ý tưởng xa hơn, ghi lại để không quên, nhưng chưa cam kết.
Việc viết rõ ba tầng này giúp bạn trả lời gọn gàng mỗi khi ai đó đề xuất thêm tính năng giữa chừng: "Ý hay đó, mình đưa vào v2 nhé" — thay vì để nó âm thầm lọt vào v1 và làm trễ tiến độ.

Tình huống thực tế

Ví dụ 1 — Sàn TMĐT (giả định kiểu Tiki/Shopee): cứu nguy nhờ version locked

Một PM tại sàn thương mại điện tử ở Hà Nội viết PRD cho tính năng "đặt hàng trước" (pre-order). Sau ba vòng nháp v0.1, v0.2, v0.3 với nhiều tranh luận về việc có cho hủy pre-order hay không, team chốt và đánh v1.0 vào ngày 5/3, kèm dòng ghi chú: "v1.0 — locked for build. Cho phép hủy trong 24h đầu."

Đến ngày 18/3, bộ phận vận hành đề nghị bỏ tính năng hủy vì kho không kịp xử lý. PM cập nhật thành v1.1 với changelog: "v1.1 (18/3) — Bỏ tính năng hủy pre-order theo yêu cầu Vận hành. Chuyển 'cho phép hủy' sang Non-goal của v1, cân nhắc cho v2."

Hai ngày sau, một engineer mới onboard đọc tài liệu, thấy ngay ở đầu trang là v1.1 và changelog giải thích vì sao không có luồng hủy. Anh ấy không cần hỏi ai, không code nhầm luồng hủy, và hiểu luôn rằng quyết định này có thể đảo ngược ở v2.

Bài học: Con số version + một dòng changelog ngắn đã thay thế cho ít nhất ba cuộc trao đổi Slack và một buổi họp giải thích. Version không chỉ đánh dấu cái gì đổi mà còn vì sao đổi — đó là phần giá trị nhất.

Ví dụ 2 — Startup SaaS Đông Nam Á: thảm họa khi "Save đè lên Save"

Một startup SaaS quản lý nhân sự ở Singapore, team 12 người, dùng Google Docs cho mọi PRD và không có quy ước version. Mọi người cứ sửa trực tiếp lên cùng một tài liệu.

Trong dự án tính năng "chấm công bằng GPS", designer thiết kế theo phiên bản spec yêu cầu "tự động chấm công khi vào bán kính 100m văn phòng". Một tuần sau, PM — sau buổi họp với khách hàng enterprise — âm thầm sửa thành "chỉ chấm công khi người dùng chủ động bấm nút, GPS chỉ để xác minh", vì lý do quyền riêng tư. PM không báo ai, vì "tài liệu đã cập nhật rồi mà". Designer không mở lại tài liệu vì nghĩ nó đã xong. Kết quả: hai tuần thiết kế và một phần code đi theo hướng "tự động" bị bỏ đi, tốn khoảng 40 giờ công vô ích.

Sau sự cố, team áp dụng quy tắc: mọi PRD bắt buộc có header gồm Version, Last updated, Status, và một bảng changelog. Mỗi thay đổi ảnh hưởng đến hành vi người dùng phải tăng version và ping những người liên quan. Sự cố tương tự không lặp lại.

Bài học: Không có versioning, "Save" trở thành thao tác phá hoại thầm lặng — nó xóa trạng thái cũ mà không để lại dấu vết, khiến những người đang dựa vào trạng thái cũ làm việc trong vô thức. Version + thông báo thay đổi là cơ chế đồng bộ "trạng thái trong đầu mọi người" với "trạng thái trên tài liệu".

Ví dụ 3 — Grab-style super-app: dùng tầng "v1 / v2 / Future" để chặn scope creep

Một PM ở một super-app gọi xe khu vực Đông Nam Á viết spec cho "ví tích điểm thưởng". Trong buổi review, ba bên — Marketing, Data, Growth — mỗi bên đề xuất thêm một thứ: đổi điểm lấy chuyến đi miễn phí, chia sẻ điểm cho bạn bè, ghép điểm với thẻ tín dụng đối tác.

Thay vì gật đầu với tất cả (và làm dự án phình từ 4 tuần lên 4 tháng), PM dùng cấu trúc tầng trong spec:

  • v1 (ship tháng này): Tích điểm khi hoàn thành chuyến đi + đổi điểm lấy voucher giảm giá.
  • v2 (quý sau): Chia sẻ điểm cho bạn bè.
  • Future: Đổi điểm lấy chuyến miễn phí; tích hợp thẻ đối tác (phụ thuộc đàm phán pháp lý).
Mỗi đề xuất đều được "đặt chỗ" ở một tầng phù hợp. Không ai cảm thấy bị từ chối, vì ý tưởng của họ đã được ghi nhận có chủ đích vào lộ trình. v1 giữ nguyên scope gọn nhẹ và ship đúng hạn.

Bài học: Phân tầng version trong nội dung là công cụ chính trị lẫn kỹ thuật. Nó cho phép bạn nói "chưa phải bây giờ" mà không nói "không", giữ thiện chí của stakeholder trong khi vẫn bảo vệ được tiến độ.

Hướng dẫn từng bước

Đây là quy trình thực tế để đưa versioning vào spec của bạn, áp dụng được ngay từ tài liệu tiếp theo:

Bước 1 — Thêm header version vào đầu mọi PRD. Ngay dưới tiêu đề, đặt một khối thông tin gọn:

Version: v1.2
Status: In build
Last updated: 27/06/2026 — Khang
Owner: Khang (PM)

Status nên là một trong các giá trị quy ước: Draft / In review / Approved / In build / Shipped. Người đọc liếc qua là biết tài liệu đang ở đâu trong vòng đời.

Bước 2 — Chọn scheme và viết quy ước vào đầu template. Quyết định: v0.x là draft, v1.0 là chốt build, minor cho sửa nhỏ, major cho đổi scope lớn. Ghi quy ước này một lần vào template chung của team để ai cũng theo cùng một luật.

Bước 3 — Duy trì một bảng changelog ở cuối tài liệu. Mỗi dòng gồm: version, ngày, người sửa, mô tả ngắn cái gì đổi và vì sao:

VersionNgàyNgườiThay đổi
v1.005/03KhangBản chốt đầu tiên, locked for build
v1.118/03KhangBỏ tính năng hủy (yêu cầu Vận hành) → Non-goal v1
v1.227/06KhangLàm rõ acceptance criteria cho lỗi mất mạng

Bước 4 — Phân tầng nội dung theo v1 / v2 / Future. Trong phần Solution hoặc Scope, tách rõ ba tầng. Cái gì ship lần này nằm ở v1 với đầy đủ chi tiết; cái gì hoãn nằm ở v2/Future với mô tả ngắn gọn.

Bước 5 — Quy ước tăng version + thông báo. Thống nhất với team: thay đổi nào ảnh hưởng hành vi người dùng hoặc phạm vi thì bắt buộc tăng version và ping người liên quan. Sửa lỗi chính tả thì không cần. Đây là nơi versioning gắn với giao tiếp — version vô nghĩa nếu thay đổi không được báo cho người cần biết.

Bước 6 — Khi ship xong, đóng băng và mở bản mới nếu cần. Khi tính năng lên production, đổi Status thành Shipped và chốt phiên bản tài liệu. Nếu bắt đầu làm vòng tiếp theo, tạo nhánh nội dung mới (hoặc tài liệu mới) đánh v2.0, link ngược về bản cũ để giữ chuỗi lịch sử.

Lỗi thường gặp & mẹo

Lỗi 1 — Đánh version nhưng không bao giờ cập nhật. Nhiều người thêm "v1.0" vào header rồi sửa tài liệu hàng chục lần mà số vẫn đứng yên. Version đứng im còn tệ hơn không có version, vì nó tạo cảm giác an toàn giả. Mẹo: gắn việc tăng version vào thói quen "trước khi nhấn Save những thay đổi đáng kể".

Lỗi 2 — Over-versioning, đánh số mọi chỉnh sửa vặt. Tăng version cho từng dấu phẩy khiến changelog ngập rác và không ai theo dõi nổi. Mẹo: chỉ tăng version khi thay đổi ý nghĩa hoặc phạm vi, không phải khi thay đổi câu chữ.

Lỗi 3 — Lẫn lộn version tài liệu với version tính năng. Viết "v2" mà không rõ là tài liệu sửa lần 2 hay giai đoạn 2 của sản phẩm. Mẹo: dùng tiền tố rõ ràng — "Doc v1.2" cho tài liệu, "Feature v2 / Future" cho phạm vi sản phẩm.

Lỗi 4 — Changelog chỉ ghi cái gì mà không ghi vì sao. "v1.1 — sửa luồng đăng nhập" gần như vô dụng sau ba tháng. Mẹo: mỗi dòng changelog phải có một mệnh đề "vì sao" — lịch sử quyết định mới là tài sản thật.

Lỗi 5 — Dựa vào tính năng "version history" tự động của tool và bỏ luôn changelog thủ công. Lịch sử tự động của Confluence/Google Docs ghi mọi keystroke nhưng không nói cho bạn biết thay đổi nào là quan trọng. Mẹo: dùng lịch sử tự động làm bản sao lưu kỹ thuật, nhưng vẫn viết changelog thủ công ở mức "quyết định" để con người đọc được.

Mẹo bổ sung: Với những tài liệu quan trọng, hãy "đóng băng" bản v1.0 bằng cách lưu một bản snapshot (export PDF hoặc tạo trang con "v1.0 — locked") đúng tại thời điểm chốt. Khi sau này có tranh chấp "spec lúc đó ghi gì", bạn có bằng chứng bất biến.

Bài tập thực hành

  • Thêm header và changelog. Lấy một PRD bạn đã viết (hoặc một bản nháp đang làm). Thêm vào đầu khối Version / Status / Last updated / Owner và một bảng changelog ở cuối. Điền lại lịch sử thay đổi đã biết của nó, mỗi dòng kèm một mệnh đề "vì sao".
  • Phân tầng v1 / v2 / Future. Cũng với tài liệu đó, đọc lại phần Solution và phân loại từng yêu cầu vào một trong ba tầng. Hãy thành thật: có thứ nào bạn đang để ở v1 mà thực ra nên đẩy sang v2 không?
  • Viết quy ước version cho team. Soạn một đoạn 5–7 dòng định nghĩa: khi nào lên v1.0, khi nào tăng minor, khi nào tăng major, và thay đổi nào bắt buộc phải thông báo. Đây sẽ là phần "Versioning convention" để gắn vào template chung.
  • Tình huống xử lý. Giả sử bạn đang ở v1.3, đã In build được 2 sprint, thì Legal yêu cầu một thay đổi lớn về cách lưu dữ liệu người dùng — đủ lớn để đảo phần Solution. Bạn sẽ đánh version mới là bao nhiêu, ghi changelog thế nào, và thông báo cho ai? Viết câu trả lời trong 5–6 câu.

Tóm tắt

Versioning trong spec không phải là thủ tục hành chính — nó là cơ chế giữ cho một tài liệu sống không trở thành nguồn hiểu lầm. Ba lý do cốt lõi để làm: PRD luôn thay đổi, mọi người cần tham chiếu đến trạng thái cụ thể, và lịch sử quyết định là tài sản quý mà bạn sẽ cần đến sáu tháng sau.

Hãy nhớ ba điều thực hành then chốt: (1) dùng scheme đơn giản — v0.x cho nháp, v1.0 cho bản chốt build, minor cho sửa nhỏ, major cho đổi scope; (2) duy trì header version và bảng changelog ghi rõ vì sao mỗi thay đổi xảy ra; (3) phân tầng nội dung theo v1 / v2 / Future để bảo vệ scope và trả lời gọn gàng mọi đề xuất phát sinh. Và tuyệt đối tách bạch version tài liệu với version sản phẩm — nhầm hai trục này là nguồn rối loạn phổ biến nhất.

Một con số version đặt đúng chỗ, kèm một dòng giải thích ngắn, có thể tiết kiệm cho team bạn hàng chục giờ tranh cãi và làm lại. Đó là khoản đầu tư rẻ nhất mà một PM có thể bỏ ra.