Bản năng trước tính năng phức tạp là "cứ thử xem" — nhưng các quyết định kiến trúc nằm ẩn ở model thứ 3, edge case thứ 5. Giá trị thực của plan mode là đưa cuộc đối thoại cấp kiến trúc sang văn bản trước khi viết code. Case thật: hệ thống bồi thẩm cộng đồng của Pickful — 3.032 dòng, 119 spec xanh, một commit duy nhất, không một lần làm lại kiến trúc về sau.
Đối diện một tính năng phức tạp, phản xạ của phần lớn người là "cứ thử viết một ít xem".
Vấn đề: hệ thống phức tạp có một đặc điểm — các quyết định kiến trúc ẩn trong model thứ 3, edge case thứ 5, luật điểm thứ 8. Cắm đầu vào code rồi va vào những quyết định ấy ở giữa chừng, quay lại sửa, sẽ đắt hơn 10 lần so với bàn cho ra nhẽ ngay từ đầu ở cấp độ văn bản.
Tôi dùng plan mode của Claude Code để làm TopicReview, hệ thống bồi thẩm cộng đồng của Pickful, và chứng kiến phiên bản cực đoan của bất đẳng thức này: một commit, 3.032 dòng, 119 spec xanh, ship một lần. Không commit nào trong hơn chục commit sau đó là làm lại kiến trúc — tất cả đều là tinh chỉnh tham số, mài UI, vá edge case. Hệ thống từ đó chạy ổn định và giờ là cốt lõi cách cộng đồng tự kiểm duyệt.
Bài này nói về vì sao tính năng phức tạp cần plan mode trước, giai đoạn plan thực sự đang làm gì, và khi nào cuộc trao đổi đủ chín để bắt đầu viết code.
TopicReview là hệ thống biểu quyết cộng đồng để quyết định có xóa một bài post chất lượng thấp hay không. Một câu tóm gọn — nhưng spec mở ra từng lớp:
Cốt lõi của độ phức tạp không nằm ở từng luật đơn lẻ — mà ở cách các luật tương tác. Mỗi luật mới thêm vào đều có thể kích hoạt rollback ở chỗ khác.
Viết thẳng tuột, những vấn đề khó nhất không phải những sự thật nhìn thấy — mà là những câu hỏi bạn không nghĩ tới việc hỏi. Đọc ngược code của TopicReview, có ít nhất bốn bức tường chắc chắn sẽ đâm vào bạn nếu bỏ plan:
Luật điều kiện bồi thẩm. Nhìn tưởng chỉ là User.jurors_and_judges.sample(12). Nhưng luật thật là: loại tác giả bài viết, loại người tố giác, loại người đã bỏ phiếu sơ thẩm (để cùng một người không bỏ phiếu cả ở kháng án). Ba tầng loại trừ. Viết model một lèo, bạn dễ sót một-hai cái.
Trả điểm trì hoãn. Phán quyết remove bình thường sẽ kích hoạt trả điểm cho bồi thẩm. Nhưng kháng án có thể lật remove — khi lật, những bồi thẩm theo phán quyết cũ lại sai phe, nên điểm phải tính lại theo phán quyết mới. Do đó phán quyết remove phải đợi cửa sổ kháng án 24h đóng rồi mới trả điểm bồi thẩm. Bỏ viết luật này, trả sớm, thế là đi thu lại điểm — phiền gấp 10 lần trả muộn.
Thiết kế interface của job lập lịch. CloseTopicReviewJob trông như "kết thúc một review". Thực tế nó xử lý ba tình huống:
# Hết hạn cửa sổ sơ thẩm
CloseTopicReviewJob.set(wait_until: voting_ends_at).perform_later(review.id)
# Trả điểm bồi thẩm trì hoãn sau phán quyết remove
CloseTopicReviewJob.set(wait: 24.hours).perform_later(review.id, award_juror_points: true)
# Hết hạn cửa sổ kháng án
CloseTopicReviewJob.set(wait: 24.hours).perform_later(review.id, appeal_id: appeal.id)
Không sắp xếp trước, bạn viết signature đầu tiên, đến case hai thì interface phải thiết kế lại.
Bức tường đắt nhất: có thể kháng án trong lúc removal tạm không? Khi phiếu remove vượt ngưỡng, bài bị ẩn ngay (tạm), nhưng cả review vẫn ở trạng thái voting — chưa decided. Người dùng có thể kháng án bây giờ không?
decided và appealedMột quyết định đó lan ngược lại và thay đổi cả kiểm tra tham số trong open_appeal! và logic state machine. Quyết định giữa chừng = viết lại nửa hệ thống.
Plan mode của Claude Code là một chế độ không cho viết code — Claude có thể đọc repo, nghĩ cách, trao đổi với bạn, nhưng mọi sửa file đều bị hard-block cho đến khi bạn duyệt một plan.
Ràng buộc máy móc đó chính là điểm chính: nó ép cuộc trao đổi cấp kiến trúc xảy ra ở văn bản.
Vài việc giai đoạn plan đang làm trong thực tế:
1. Vẽ state machine và vai trò. 5 trạng thái, 4 vai trò (bồi thẩm / thẩm phán / tác giả / người tố giác), mỗi vai trò làm được gì và không làm được gì ở mỗi trạng thái — tất cả gói trong vài dòng markdown. Vài dòng vs. hàng chục file. Chi phí thay đổi cách nhau hai bậc lớn.
2. Đi qua luồng của từng vai trò:
Chỗ nào đi không trôi, câu hỏi ẩn lập tức lộ ra: "bồi thẩm có thấy phiếu của bồi thẩm khác không?", "tác giả làm gì được trong lúc removal tạm?"
3. Thẩm vấn edge case. Giai đoạn plan không thiết kế "đường bình thường" — nó cố ý hỏi những câu mà bình thường không nghĩ đến:
finalize — race có trả điểm hai lần không? (Thêm trường juror_points_awarded để idempotent.)95% những câu hỏi này không tự nhiên nổi lên khi viết code. Giai đoạn plan ép bạn đi qua từng câu.
4. Sổ cái điểm. Hệ thống điểm phức tạp đến mức thảo luận thôi là không đủ — phải thực sự vẽ bảng: mỗi dòng điểm với điều kiện kích hoạt + số lượng + đường rollback. Khi sổ cái cân, tất cả edge case (hoàn phạt tạm, hoàn kháng án, bonus) đều đối chiếu được.
Một tiêu chí cứng:
closed (keep / remove / warn × có kháng án / không × provisional / không) — kể được mạch lạc từ đầu đến cuốiĐạt chuẩn đó, viết code trở thành dịch plan sang Ruby.
Ship đầu tiên của hệ thống:
d162f1e Add community moderation system with jury/judge review and appeals
63 files changed, 3032 insertions(+)
119 specs, 0 failures
63 file, 3.032 dòng, 119 spec xanh. Ship một lần.
Hơn chục commit sau xác nhận kiến trúc đứng vững:
Apply legacy penalty (1pt) for posts created before 2026-03-26
Fix topic review appeal bugs: window mismatch and verdict not updated
Add topic_removed status for posts removed by community review
Default to keep verdict when review expires with zero votes
Reduce provisional removal penalty to 1pt during trial period
Allow topic creator to withdraw active reviews
Add handled tab to jury dashboard, fix tabs styling
Mỗi cái đều là tinh chỉnh / vá / mài / thêm tính năng nhỏ. Không cái nào là "quay lại thiết kế lại state machine" hay "mô hình điểm phải đổi". Bộ khung đứng đúng.
Đây là phần thưởng thực sự của plan: thi công không bị cắt ngang. Không có "khoan đã, đường này xử lý ra sao" — plan đã nói. Không có "edge case này tôi không nghĩ tới" — plan đã hỏi. Không có "interface này phải thiết kế lại" — plan đã gọn.
Nhiều giờ viết code, chỉ làm một việc: dịch thiết kế rõ ràng xuống bàn phím.
Không phải nhiệm vụ nào cũng đáng plan:
Lợi ích của plan mode đến từ hạ chi phí viết lại. Nếu công việc không có rủi ro viết lại, plan chỉ là chi phí thừa.
"Nghĩ trước khi làm" nghe như lời khuyên về tính cách. Plan mode không phải như vậy.
Plan mode là làm cho cuộc trao đổi cấp kiến trúc xảy ra ở nơi 10 chữ thay đổi được — không phải ở nơi 30 file phải thay đổi.
Dưới độ phức tạp, câu nói cũ của ngành "words are cheap, code is expensive" cần được đảo — không có nghĩa "cứ viết code trước rồi xem" (điều đó chỉ đúng khi chênh lệch chi phí nhỏ), mà tận dụng chênh lệch chi phí: đưa cuộc trao đổi đắt lên trước, trong môi trường rẻ.
3.000 dòng ship một lần có cảm giác rất đã. Không phải vì tôi viết nhanh — mà vì plan biến "viết" thành hành động thuần cơ học.