8 phút

Spec tính năng từ mã với Claude Code: một quy trình đơn giản

Tìm hiểu cách tạo spec tính năng từ mã với Claude Code bằng cách trích hành vi thực từ routes và components, rồi tạo spec sống và danh sách gaps.

Spec tính năng từ mã với Claude Code: một quy trình đơn giản

Tại sao bạn cần spec khớp với mã

Mọi người tranh luận về app vì họ nhớ những phiên bản khác nhau. Support nhớ ticket gần nhất. Sales nhớ đường dẫn demo. Kỹ sư nhớ tính năng như nó được thiết kế. Hỏi ba người và bạn có ba câu trả lời chắc chắn, nhưng không ai trùng với bản build hiện tại.

Theo thời gian, mã trở thành nguồn duy nhất luôn cập nhật. Tài liệu trôi dạt, ticket đóng, và các sửa nhanh chồng chất. Một route có quy tắc xác thực mới. Một toggle UI thay đổi mặc định. Một handler bắt đầu trả các lỗi khác. Không ai cập nhật spec vì nó cảm thấy là tùy chọn, và mỗi thay đổi đều quá nhỏ để ghi nhận.

Điều đó tạo ra những vấn đề dễ đoán. Team phát hành thay đổi làm vỡ các trường hợp cạnh mà họ không biết tồn tại. QA test con đường thuận lợi và bỏ sót quy tắc nằm sâu trong handler. Đồng nghiệp mới sao chép hành vi từ UI mà không hiểu ràng buộc thực sự. Stakeholder tranh luận ý kiến thay vì chỉ vào hành vi đã thỏa thuận.

Kết quả tốt không phải là một tài liệu hoàn hảo. Là sự rõ ràng chung. Ai cũng nên trả lời được: "Chuyện gì xảy ra nếu tôi làm X?" và "Hệ thống đảm bảo điều gì?" mà không phải đoán. Bạn sẽ có ít bất ngờ hơn, vòng review ngắn hơn và ít khoảnh khắc "Khoan, nó đã làm được rồi" vì cả đội đang nhìn cùng một sự thật.

Khi spec khớp với mã, việc lên kế hoạch thay đổi trở nên an toàn. Bạn có thể nhận ra thứ nào ổn định, thứ nào là tai nạn, và thứ gì còn thiếu trước khi phát hành.

Spec sống và danh sách gaps là gì

Spec sống là một mô tả ngắn, có thể chỉnh sửa về những gì app thực sự làm hôm nay. Nó không phải tài liệu một lần. Nó thay đổi mỗi khi hành vi thay đổi, nên team có thể tin tưởng.

Khi người ta nói về spec tính năng viết từ mã (ví dụ, dùng Claude Code), mục tiêu đơn giản: đọc hành vi thực từ routes, handlers và màn hình, rồi viết lại bằng ngôn ngữ rõ ràng.

Một spec sống hữu ích tập trung vào những gì người dùng nhìn thấy và những gì hệ thống cam kết. Nó nên bao gồm:

  • Hành vi nhìn thấy được bởi người dùng (chuyện gì xảy ra khi ai đó nhấn, submit, đăng nhập)
  • Quy tắc và ràng buộc (trường bắt buộc, giới hạn, phép tính)
  • Trường hợp cạnh (trạng thái rỗng, lỗi, retry, timeout)
  • Quyền truy cập (ai được xem, tạo, chỉnh sửa, xóa)
  • Kết quả quan trọng (email gửi, bản ghi tạo, trạng thái thay đổi)

Những điều không nên ghi là cách mã được tổ chức. Nếu bạn bắt đầu đặt tên file và kế hoạch refactor, bạn đang sa vào chi tiết triển khai. Tránh:

  • Tên hàm và class, cây component
  • Tranh luận kiến trúc và kế hoạch viết lại

Danh sách gaps là riêng biệt. Đó là một danh sách nhỏ các bất đồng và những điều không rõ bạn tìm thấy khi viết spec.

  • Bug là: mã vi phạm hành vi hiện tại hoặc một quy tắc đã thỏa thuận.
  • Yêu cầu tính năng là: bạn muốn hành vi mới.
  • Gap là: bạn không thể biết hành vi đúng là gì, hoặc hành vi không nhất quán trên các màn hình/role.

Ví dụ: một route từ chối file lớn hơn 10MB nhưng UI nói 25MB. Đó là một gap cho đến khi team quyết định quy tắc nào là thực và cập nhật code hoặc spec.

Chọn phạm vi và định dạng spec đơn giản

Bắt đầu nhỏ. Nếu bạn cố gắng tài liệu hóa cả app, bạn sẽ kết thúc với một đống ghi chú không ai tin tưởng. Chọn một lát nhỏ mà người dùng có thể mô tả bằng một câu, như "mời đồng đội", "thanh toán", hoặc "đặt lại mật khẩu." Phạm vi tốt là một vùng tính năng đơn, một module, hoặc một hành trình người dùng từ điểm vào đến kết quả.

Chọn điểm vào dựa trên nơi sự thật tồn tại:

  • Nếu bạn cần quy tắc thực, bắt đầu từ routes và handlers.
  • Nếu bạn cần trải nghiệm thực, bắt đầu từ entry point UI.
  • Nếu tính năng rối, bắt đầu từ trang/bộ điều khiển cấp cao nhất và làm ra ngoài.

Trước khi đọc mã, thu thập vài đầu vào để sai lệch nổi bật nhanh: tài liệu API hiện có, ghi chú sản phẩm cũ, ticket support, và các "điểm đau" đã biết mọi người than phiền. Những thứ này không ghi đè mã, nhưng giúp bạn nhận ra trạng thái thiếu như lỗi, case cạnh và quyền truy cập.

Giữ định dạng spec nhàm và nhất quán. Team dễ đồng thuận khi mọi spec đọc giống nhau.

Mẫu spec (lặp cho mỗi luồng hướng người dùng)

  • Purpose: mục tiêu người dùng muốn đạt
  • Entry points: nơi luồng bắt đầu (URL, menu, nút)
  • Preconditions: auth, role, dữ liệu cần có
  • Main flow: 5–10 bước bằng ngôn ngữ đơn giản
  • Data and side effects: bản ghi tạo/cập nhật, email, log
  • Errors and edge cases: chuyện gì xảy ra khi có lỗi
  • Open questions: hành vi chưa rõ cần xác nhận

Dùng cấu trúc này lặp lại sẽ giúp spec dễ đọc, dễ so sánh và dễ cập nhật.

Trích hành vi từ routes và handlers

Bắt đầu từ điểm vào server. Routes và handlers cho thấy "app làm gì" một cách cụ thể: ai gọi được, họ phải gửi gì, họ nhận lại gì, và hệ thống thay đổi gì.

Liệt kê các route trong phạm vi và ánh xạ mỗi route tới mục đích người dùng. Đừng viết "POST /api/orders." Hãy viết "Đặt hàng" hoặc "Lưu nháp." Nếu bạn không thể đặt tên mục đích bằng ngôn ngữ đơn giản, đó đã là một gap spec.

Khi đọc từng handler, ghi các input và quy tắc xác thực thành yêu cầu hướng người dùng. Bao gồm trường bắt buộc, định dạng cho phép, và các quy tắc gây lỗi thực sự. Ví dụ: "Email phải hợp lệ", "Số lượng phải ít nhất 1", "Ngày bắt đầu không thể ở quá khứ."

Viết kiểm tra auth và role theo cách tương tự. Thay vì "middleware: requireAdmin," ghi: "Chỉ admins mới có thể hủy mọi đơn. Người dùng thường chỉ hủy đơn của chính họ trong vòng 10 phút." Nếu mã kiểm tra ownership, feature flag, hoặc ranh giới tenant, cũng đưa vào.

Rồi ghi các output và kết quả. Thành công trả gì (ID được tạo, object cập nhật)? Các lỗi phổ biến trông ra sao (401 chưa đăng nhập, 403 không được phép, 404 không tìm thấy, 409 xung đột, 422 lỗi xác thực)?

Cuối cùng, ghi side effects vì chúng là một phần của hành vi: bản ghi được tạo/cập nhật, email/notification gửi, event publish, job nền xếp hàng, và bất cứ điều gì kích hoạt luồng khác. Những chi tiết này ngăn ngừa bất ngờ khi team sau này dựa vào spec.

Trích hành vi từ component và luồng UI

Routes cho bạn biết app có thể làm gì. Components cho bạn biết người dùng thực sự trải nghiệm ra sao. Xem UI như một phần của hợp đồng: cái gì hiển thị, cái gì bị chặn, và chuyện gì xảy ra khi có lỗi.

Bắt đầu bằng cách tìm các màn hình entry cho tính năng. Tìm component trang, layout wrapper, và vài component "quyết định" kiểm soát fetching, quyền truy cập, và điều hướng. Đó thường là nơi hành vi thực nằm.

Khi đọc component, ghi các quy tắc người dùng cảm nhận được: khi nào hành động bị disable, bước bắt buộc, trường điều kiện, trạng thái loading, và cách lỗi hiện (lỗi trường inline so với toast, auto-retry, nút "thử lại"). Ghi cả hành vi state và cache như dữ liệu cũ hiện trước, cập nhật lạc quan, hoặc timestamp "lần lưu cuối".

Chú ý các luồng ẩn thay đổi im lặng giao diện người dùng. Tìm feature flag, experiment bucket, và cổng chỉ admin. Ghi cả redirect im lặng, như gửi người chưa đăng nhập đến sign-in hoặc gửi người không có quyền đến màn hình upgrade.

Một ví dụ cụ thể: trên màn hình "Thay đổi Email", ghi rằng nút Lưu giữ disable cho đến khi email hợp lệ, spinner hiện trong lúc request, thành công bật banner xác nhận, và lỗi xác thực backend hiển thị dưới input. Nếu mã hiển thị flag như newEmailFlow, ghi cả hai biến thể và khác biệt của chúng.

Viết mỗi luồng UI thành các bước ngắn (người dùng làm gì, UI phản hồi ra sao) và giữ điều kiện cùng lỗi ở ngay cạnh bước ảnh hưởng. Điều này làm spec dễ đọc và giúp phát hiện gap nhanh hơn.

Biên soạn phát hiện thành spec dễ đọc

Xây prototype React
Bắt đầu app React từ spec ngôn ngữ tự nhiên và lặp nhanh trong chat.

Ghi chú thô từ routes và components hữu ích nhưng khó thảo luận. Viết lại những gì bạn quan sát thành spec mà PM, designer, QA và engineer đều có thể đọc và đồng ý.

Mẫu thực tế là một user story mỗi route hoặc màn hình. Giữ nhỏ và cụ thể. Ví dụ: "Là người dùng đã đăng nhập, tôi có thể đặt lại mật khẩu để lấy lại quyền truy cập." Nếu mã cho thấy hành vi khác theo role (admin vs user), tách thành các story riêng thay vì giấu trong chú thích.

Rồi viết acceptance criteria phản chiếu các đường dẫn mã thực, không phải sản phẩm lý tưởng. Nếu handler trả 401 khi token thiếu, đó là một tiêu chí. Nếu UI disable submit cho đến khi field hợp lệ, đó là một tiêu chí.

Bao gồm quy tắc dữ liệu bằng ngôn ngữ đơn giản, đặc biệt những thứ gây bất ngờ: giới hạn, thứ tự, tính duy nhất, trường bắt buộc. "Username phải duy nhất (kiểm tra khi lưu)" rõ hơn "unique index."

Trường hợp cạnh thường là khác biệt giữa tài liệu đẹp và tài liệu hữu dụng. Ghi rõ trạng thái rỗng, giá trị null, retry, timeout, và người dùng thấy gì khi API gọi thất bại.

Khi gặp điều chưa biết, đánh dấu thay vì đoán:

  • Unknown: thông điệp nào nên hiện khi email không tìm thấy?
  • Unknown: có cho phép 0 items hay bắt buộc ít nhất 1?
  • Unknown: lỗi này hiển thị cho người dùng hay chỉ log?

Những marker này biến thành các câu hỏi nhanh cho team thay vì giả định im lặng.

Tạo danh sách gaps mà không biến nó thành backlog

Danh sách gaps không phải Jira thứ hai. Là một bản ghi ngắn, dựa trên bằng chứng của nơi mã và hành vi mong muốn không khớp, hoặc nơi không ai giải thích rõ "đúng" là gì. Nếu làm tốt, nó trở thành công cụ để đạt đồng thuận, không phải cuộc chiến lập kế hoạch.

Nghiêm khắc về điều nào tính là gap:

  • Hành vi không rõ: app làm gì đó nhưng quy tắc không được viết ở đâu
  • Không nhất quán: hai nơi hành xử khác nhau cho cùng một trường hợp
  • Quy tắc thiếu: có case cạnh nhưng không có quyết định rõ trong mã hay docs

Khi log một gap, gồm ba phần để giữ nó cụ thể:

  • Type: bug (mã có vẻ sai) hoặc missing decision (ý định không rõ)
  • Impact: nhầm lẫn người dùng, rủi ro bảo mật, mất dữ liệu, hay nhỏ
  • Evidence: nơi bạn thấy và quan sát (route/handler/component)

Bằng chứng là thứ giữ danh sách khỏi biến thành ý kiến. Ví dụ: "POST /checkout/apply-coupon chấp nhận coupon hết hạn, nhưng CouponBanner.tsx chặn chúng ở UI. Tác động: doanh thu và nhầm lẫn người dùng. Loại: bug hoặc missing decision (xác nhận quy tắc mong muốn)."

Giữ nó ngắn. Đặt giới hạn cứng, ví dụ 10 mục cho lần đầu. Nếu tìm 40 vấn đề, gom thành các mẫu (không nhất quán validate, kiểm tra quyền, trạng thái rỗng) và giữ vài ví dụ hàng đầu.

Tránh đặt ngày và lịch trong gaps. Nếu cần ai chịu trách nhiệm, ghi nhẹ: ai nên quyết định (product) hoặc ai có thể xác minh (engineering), rồi chuyển việc lập kế hoạch thực sự sang backlog.

Ví dụ: tài liệu hóa một tính năng thực từ mã

Tìm gap trước khi phát hành
So sánh hành vi UI và API trong cuộc trò chuyện và liệt kê các chênh lệch có bằng chứng.

Chọn phạm vi nhỏ, lưu lượng cao: checkout với mã khuyến mãi và tùy chọn vận chuyển. Mục tiêu không phải viết lại sản phẩm, chỉ nắm những gì app làm hôm nay.

Bắt đầu với route backend. Thường quy tắc xuất hiện ở đó trước. Bạn có thể thấy route như POST /checkout/apply-promo, GET /checkout/shipping-options, và POST /checkout/confirm.

Từ các handler đó, viết hành vi bằng lời đơn giản:

  • Mã khuyến mãi được validate ở server (hết hạn, giới hạn sử dụng, điều kiện khách hàng).
  • Tổng tiền được tính lại sau khi áp mã, nhưng chỉ sau khi kiểm tra tồn kho.
  • Tùy chọn vận chuyển phụ thuộc vào điểm đến, cân nặng, và nếu có món nào bị đánh dấu "restricted".
  • Confirm thất bại nếu hàng trong line item thay đổi kể từ khi giỏ được tải.
  • Thuế tính sau khi chọn vận chuyển (không phải khi áp mã).

Rồi kiểm tra component UI. PromoCodeInput có thể chỉ cập nhật tổng khi có phản hồi thành công và lỗi hiển thị inline dưới input. ShippingOptions có thể chọn tự động phương án rẻ nhất khi tải lần đầu và kích hoạt cập nhật bảng giá đầy đủ khi người dùng thay đổi.

Giờ bạn có spec dễ đọc và một danh sách gaps nhỏ. Ví dụ: thông điệp lỗi giữa route mã và UI khác nhau ("Invalid code" vs "Not eligible"), và không ai biết quy tắc làm tròn thuế rõ ràng (theo dòng hay tổng đơn).

Trong hoạch định, team đồng ý về thực tế trước, rồi quyết định thay đổi. Thay vì tranh luận ý kiến, bạn xem hành vi đã được tài liệu, chọn một bất nhất để sửa, và để phần còn lại là "hành vi hiện tại đã biết" cho đến khi đáng để xem lại.

Xác thực spec với team và giữ nó luôn cập nhật

Spec chỉ có ích nếu team đồng ý nó khớp với thực tế. Làm một buổi đọc ngắn với một kỹ sư và một người sản phẩm. Giữ ngắn: 20–30 phút tập trung vào người dùng có thể làm gì và hệ thống phản ứng ra sao.

Trong buổi đọc, biến các phát biểu thành câu hỏi có/không. "Khi user vào route này, chúng ta luôn trả 403 nếu không có session chứ?" "Trạng thái rỗng này có chủ ý không?" Điều này tách hành vi mong muốn ra khỏi hành vi vô ý lọt vào theo thời gian.

Đồng ý về từ ngữ trước khi chỉnh sửa. Dùng từ người dùng thấy trong UI (nhãn nút, tiêu đề trang, thông báo). Thêm tên nội bộ chỉ khi giúp kỹ sư tìm mã (tên route, tên component). Điều này tránh lệch lạc như product nói "Workspace" trong khi spec dùng "Org."

Để giữ nó cập nhật, rõ trách nhiệm và nhịp độ:

  • Owner spec: một người merge thay đổi spec (thường feature owner hoặc tech lead)
  • Kích hoạt cập nhật: khi merge PR cho thay đổi hành vi, hoặc mỗi release
  • Kiểm tra nhanh: thêm checkbox "spec đã cập nhật?" vào template PR
  • Lưu trữ: giữ nó gần mã để nó thay đổi cùng mã

Nếu dùng công cụ như Koder.ai, snapshot và rollback giúp so sánh "trước" và "sau" khi bạn cập nhật spec, đặc biệt sau refactor lớn.

Sai lầm và bẫy thường gặp

Cách nhanh nhất để mất niềm tin vào spec là mô tả sản phẩm bạn muốn, không phải sản phẩm bạn có. Giữ quy tắc cứng: mỗi phát biểu phải có thể chỉ vào mã hoặc màn hình thực tế.

Một bẫy phổ biến là sao chép cấu trúc mã vào tài liệu. Một spec đọc như "Controller -> Service -> Repository" không phải spec, mà là bản đồ thư mục. Viết bằng ngôn ngữ hướng người dùng: gì kích hoạt hành động, người dùng thấy gì, lưu gì, và lỗi trông ra sao.

Quyền và role thường bị bỏ qua đến cuối cùng, rồi mọi thứ vỡ. Thêm quy tắc truy cập sớm, ngay cả khi lộn xộn. Ghi role nào được xem, tạo, sửa, xóa, xuất, hay phê duyệt, và nơi quy tắc được thực thi (chỉ UI, chỉ API, hoặc cả hai).

Đừng bỏ qua non-happy paths. Hành vi thực ẩn trong retry, thất bại từng phần, và quy tắc theo thời gian như hết hạn, cooldown, job theo lịch, hoặc giới hạn "mỗi ngày một lần". Đối xử chúng như hành vi hạng nhất.

Một cách nhanh để lộ gap là kiểm tra:

  • Lỗi xác thực và thông điệp lỗi chính xác người dùng thấy
  • Xử lý gửi trùng (idempotency)
  • Công việc nền (queue, cron) và chuyện gì xảy ra nếu nó thất bại
  • Vấn đề đồng thời (hai người sửa cùng bản ghi)
  • Hành vi theo thời gian (timeout, expiration, rate limit)

Cuối cùng, giữ danh sách gaps chuyển động. Mỗi gap nên được dán nhãn: "unknown, needs decision," "bug, fix," hoặc "missing feature, plan." Nếu không có nhãn, danh sách sẽ đình trệ và spec ngừng "sống."

Checklist nhanh trước khi chia sẻ spec

Snapshot và rollback nhanh
Thử nghiệm trên một tính năng và khôi phục nhanh nếu hành vi bị lệch.

Lướt nhanh để kiểm tra độ rõ ràng, phạm vi và khả năng hành động. Người không viết spec phải hiểu tính năng hoạt động hôm nay và điều gì chưa rõ.

Rõ ràng và đồng thuận chung

Đọc spec như một đồng đội mới ngày đầu. Nếu họ có thể tóm tắt tính năng trong một phút, bạn gần xong. Nếu họ liên tục hỏi "chỗ này bắt đầu ở đâu?" hoặc "đường chính là gì?" thì mở phần mô tả đầu.

Kiểm tra:

  • Bài kiểm một trang: mở đầu nêu mục tiêu người dùng, nơi luồng bắt đầu và kết thúc.
  • Role và truy cập: role chính và mỗi role có thể/cannot làm gì.
  • Kết quả: thành công trông ra sao và người dùng thấy gì khi thất bại (thông báo, chuyển hướng, retry).
  • Biên và giới hạn: kích thước, giới hạn tốc độ, timeout, quy tắc validate, và chuyện gì xảy ra khi thiếu dữ liệu.
  • Ngôn ngữ: dùng thuật ngữ người dùng trước; định nghĩa thuật ngữ chuyên môn không tránh được một lần.

Gaps hữu ích, không ồn ào

Mỗi gap nên cụ thể và có thể kiểm tra. Thay vì "Xử lý lỗi không rõ", viết: "Nếu nhà cung cấp thanh toán trả 402, UI hiện toast chung; xác nhận thông điệp mong muốn và hành vi retry." Thêm một hành động tiếp theo duy nhất (hỏi product, thêm test, kiểm tra log) và ghi ai trả lời.

Bước tiếp theo thực hiện trong tuần này

Chọn một khu vực tính năng và giới hạn thời gian 60 phút. Chọn thứ gì đó nhỏ nhưng thực (login, checkout, search, một màn hình admin). Viết một câu mô tả phạm vi: bao gồm gì và ngoại trừ gì.

Chạy workflow một lần từ đầu đến cuối: lướt qua các route/handler chính, truy vết luồng UI chính, và ghi lại hành vi quan sát được (input, output, validate, trạng thái lỗi). Nếu bí, log câu hỏi là một gap và tiếp tục.

Khi xong, chia sẻ spec để team comment, và đặt một quy tắc: mọi thay đổi hành vi đã ship phải cập nhật spec trong cùng khoảng giao hàng, dù chỉ năm dòng.

Giữ gaps tách khỏi backlog. Gom chúng vào "hành vi chưa rõ," "hành vi không nhất quán," và "thiếu test," rồi xem nhanh hàng tuần để quyết điều gì quan trọng ngay bây giờ.

Nếu việc soạn thảo và lặp cảm thấy chậm, công cụ chat-based như Koder.ai có thể giúp bạn có bản nháp ban đầu nhanh. Mô tả tính năng, dán đoạn mã hoặc tên route chính, tinh chỉnh câu chữ trong cuộc trò chuyện, và xuất nguồn khi cần. Mục tiêu là tốc độ và rõ ràng chung, không phải quy trình to hơn.

Câu hỏi thường gặp

Where do I start if I want to write a feature spec from existing code?

Bắt đầu với một phần nhỏ, hiển thị cho người dùng (ví dụ: “đặt lại mật khẩu” hoặc “mời đồng đội”). Đọc routes/handlers để nắm các quy tắc và kết quả, rồi đọc luồng UI để ghi lại trải nghiệm người dùng thực tế (trạng thái bị disable, lỗi, chuyển hướng). Ghi lại bằng một mẫu nhất quán và lưu các điều chưa rõ trong một danh sách gaps riêng.

Should the spec describe what the product should do, or what the code does today?

Mặc định: xem hành vi hiện tại của mã là nguồn sự thật và tài liệu hóa nó.

Nếu hành vi trông như vô ý hay không đồng nhất, đừng “sửa” trong spec — đánh dấu nó là một gap kèm bằng chứng (nơi bạn thấy và nó hoạt động thế nào), rồi lấy quyết định để cập nhật code hoặc spec.

What’s a simple spec format that stays readable as the app grows?

Giữ cho nó nhàm và lặp lại. Một mẫu thực dụng là:

  • Purpose
  • Entry points
  • Preconditions (auth/role/data)
  • Main flow (5–10 bước)
  • Data and side effects
  • Errors and edge cases
  • Open questions

Cách này giúp spec dễ đọc và làm lộ các bất đồng nhanh hơn.

How do I turn handler validation and auth checks into plain-language requirements?

Viết quy tắc dưới dạng yêu cầu người dùng, chứ không phải chú thích mã.

Ví dụ:

  • “Email phải hợp lệ”
  • “Số lượng phải ít nhất là 1”
  • “Chỉ admins mới hủy được mọi đơn; người dùng thường chỉ hủy đơn của họ trong 10 phút”

Ghi lại điều gì sẽ gây ra lỗi và người dùng sẽ thấy gì khi lỗi đó xảy ra.

What outputs and side effects should a spec include?

Tập trung vào những gì quan sát được:

  • Kết quả thành công (thay đổi gì, người dùng thấy gì)
  • Các loại lỗi phổ biến (chưa đăng nhập, không được phép, không tìm thấy, lỗi xác thực)
  • Side effects (bản ghi cập nhật, email/thông báo gửi, job nền được xếp hàng)

Side effects quan trọng vì chúng ảnh hưởng tới các tính năng khác và kỳ vọng hỗ trợ/ops.

What if the UI and backend disagree (like different file size limits)?

Nếu UI chặn điều gì mà API cho phép (hoặc ngược lại), ghi nó thành một gap cho đến khi quyết định được đưa ra.

Ghi lại:

  • UI nói/hiển thị gì
  • Backend bắt buộc gì
  • Tác động (nhầm lẫn, bảo mật, dữ liệu)

Rồi đồng ý một quy tắc duy nhất và cập nhật cả code lẫn spec cho khớp.

How do I write a gaps list without turning it into a planning fight?

Giữ danh sách gaps ngắn và có bằng chứng. Mỗi mục nên có:

  • Type: bug vs missing decision
  • Impact: nhỏ hay nghiêm trọng (nhầm lẫn, bảo mật, mất dữ liệu)
  • Evidence: nơi bạn quan sát (route/handler/component) và hành vi chính xác

Tránh lập lịch hay biến nó thành backlog thứ hai.

Which edge cases are most important to capture in a living spec?

Ghi rõ thay vì giấu:

Bao gồm:

  • Trạng thái rỗng (không kết quả, không có quyền)
  • Retry/timeouts và người dùng có thể làm gì tiếp
  • Gửi trùng (double-click, refresh)
  • Đồng thời (hai người sửa cùng bản ghi)
  • Quy tắc theo thời gian (hết hạn, cooldown)

Chúng thường là nơi phát sinh lỗi và bất ngờ.

How do I validate the spec with the team so people trust it?

Ngắn gọn: đọc trong 20–30 phút với một engineer và một product person.

Biến các phát biểu thành câu hỏi có/không (ví dụ: “Khi người dùng gọi route này, chúng ta luôn trả 403 nếu không có session chứ?”). Đồng ý về từ vựng theo UI (nhãn, thông báo) để mọi người hiểu cùng một nghĩa.

How do I keep the spec “living” instead of letting it drift again?

Đặt spec gần mã và làm cho cập nhật là một phần của việc phát hành.

Mặc định thực tế:

  • Một owner rõ ràng người merge thay đổi spec
  • Kích hoạt cập nhật: bất kỳ thay đổi hành vi nào được merge hoặc mỗi release
  • Thêm mục checklist PR: “Spec đã cập nhật?”
  • Giữ gaps riêng và xem xét định kỳ

Mục tiêu là sửa đổi nhỏ, thường xuyên — không phải viết lại lớn.

Related posts