Think Like a Developer #5: State Thinking - "Mọi thứ đều có trạng thái"
App đặt hàng của bạn chạy ngon. Khách chọn món, bấm thanh toán, tiền trừ thành công. Bạn khoe với bạn bè.
Rồi một ngày, khách nhắn: "Tôi thanh toán rồi mà đơn vẫn hiện chưa thanh toán."
Bạn mở app kiểm tra. Đúng thật. Tiền đã trừ. Nhưng đơn hàng vẫn nằm ở trạng thái "chờ thanh toán". Khách bấm thanh toán lần nữa. Trừ tiền lần nữa. Hai lần tiền, một đơn hàng. Khách nổi điên.
Bạn nhờ AI sửa. AI sửa xong, giờ đơn hàng nhảy thẳng sang "đã giao" ngay khi thanh toán. Bỏ qua luôn bước "đang chuẩn bị" và "đang giao". Sửa chỗ này, vỡ chỗ kia.
Nghe quen không? Đây là một trong những lỗi phổ biến nhất khi vibe code. Và nguyên nhân gốc rễ không phải code. Mà là bạn chưa nghĩ về state.
Mọi thứ trong phần mềm đều có trạng thái
State, hay trạng thái, là tình trạng hiện tại của một thứ gì đó tại một thời điểm.
Nghe trừu tượng? Thực ra bạn sống với state mỗi ngày.
Đèn giao thông có 3 state: đỏ, vàng, xanh. Tại bất kỳ thời điểm nào, đèn chỉ ở đúng 1 trong 3 state đó. Không bao giờ vừa đỏ vừa xanh. Và nó chuyển theo thứ tự: xanh chuyển vàng, vàng chuyển đỏ, đỏ chuyển xanh. Không bao giờ nhảy từ xanh thẳng sang đỏ.
Giờ nghĩ về Grab. Khi bạn đặt xe, chuyến đi của bạn đi qua bao nhiêu state?
Đang tìm tài xế
Đã tìm được tài xế
Tài xế đang đến điểm đón
Tài xế đã đến
Đang di chuyển
Đã đến nơi
Hoàn thành (đã thanh toán + đánh giá)
Và có thêm 1 state đặc biệt: Đã hủy. Có thể xảy ra ở bước 1, 2, 3, hoặc 4.
7 state chính + 1 state hủy. Mỗi state có giao diện riêng, hành động riêng, và quy tắc chuyển sang state tiếp theo riêng.
Developer nhìn thấy điều này ngay từ đầu. Người no-code thường không nghĩ tới. Và đó là lý do app hay bị lỗi "trạng thái sai".
Tại sao không nghĩ về state sẽ gây lỗi?
Quay lại ví dụ đầu bài. App đặt hàng. Bạn prompt AI: "Tạo hệ thống đặt hàng: khách chọn món, bấm đặt, thanh toán online."
AI tạo ra. Nhưng vì bạn không nói rõ các state, AI tự đoán. Và nó đoán thiếu.
Nó có thể chỉ có 2 state: "chưa thanh toán" và "đã thanh toán". Không có "đang xử lý thanh toán". Nên khi payment gateway mất 3 giây để xác nhận, đơn hàng vẫn ở "chưa thanh toán". Khách thấy vậy, bấm thanh toán lần nữa. Trừ tiền 2 lần.
Hoặc AI có thể nhảy state: từ "đã thanh toán" thẳng sang "đã giao", bỏ qua "đang chuẩn bị" và "đang giao". Kết quả: khách thấy "đã giao" trong khi đồ ăn còn chưa nấu.
Hoặc tệ hơn: AI không có state "đã hủy". Khách muốn hủy đơn nhưng không có cách nào hủy. Hoặc hủy rồi mà hệ thống vẫn chuẩn bị đơn.
Tất cả những lỗi này đều có chung một nguyên nhân: không liệt kê hết state từ đầu.
Ví dụ đời thường: Gửi bưu phẩm
Bạn gửi một gói hàng qua bưu điện. Từ lúc gửi đến lúc người nhận nhận được, gói hàng đi qua bao nhiêu trạng thái?
Đã tiếp nhận (bưu điện nhận hàng)
Đang vận chuyển (trên đường đi)
Đã đến bưu cục đích
Đang giao
Đã giao thành công
Và các trạng thái đặc biệt: Giao không thành công (không có người nhận), Đang hoàn trả, Đã hoàn trả.
Nếu hệ thống tracking chỉ có 2 state "đang gửi" và "đã nhận", bạn sẽ không biết hàng đang ở đâu, có vấn đề gì, hay khi nào nhận được. Vô dụng.
Phần mềm cũng vậy. Càng nhiều state được xác định rõ, app càng ít lỗi và người dùng càng hiểu chuyện gì đang xảy ra.
Cách liệt kê state trước khi nhờ AI build
Developer có một kỹ thuật đơn giản: vẽ State Diagram. Nghe fancy nhưng thực ra chỉ là trả lời 3 câu hỏi:
Câu 1: "Thứ này có bao nhiêu trạng thái?"
Lấy giấy bút. Liệt kê tất cả trạng thái có thể có. Đừng bỏ sót state lỗi, state hủy, state chờ.
Ví dụ cho đơn hàng: Chờ xác nhận, Đã xác nhận, Đang chuẩn bị, Đang giao, Đã giao, Đã hủy, Hoàn tiền
Câu 2: "Từ state này có thể chuyển sang state nào?"
Không phải state nào cũng chuyển được sang state nào. Đơn hàng "Đã giao" không thể quay lại "Đang chuẩn bị". Đơn hàng "Đã hủy" không thể nhảy sang "Đang giao".
Viết ra: Chờ xác nhận -> Đã xác nhận HOẶC Đã hủy Đã xác nhận -> Đang chuẩn bị HOẶC Đã hủy Đang chuẩn bị -> Đang giao Đang giao -> Đã giao HOẶC Giao thất bại Giao thất bại -> Đang giao (giao lại) HOẶC Hoàn tiền Đã giao -> Hoàn tiền (nếu có vấn đề)
Câu 3: "Điều gì kích hoạt việc chuyển state?"
Mỗi lần chuyển state đều có một trigger: Khách bấm đặt -> Chờ xác nhận Shop bấm xác nhận -> Đã xác nhận Shop bấm "bắt đầu chuẩn bị" -> Đang chuẩn bị Shipper bấm "đã lấy hàng" -> Đang giao Shipper bấm "đã giao" -> Đã giao Khách bấm "hủy" -> Đã hủy (chỉ khi chưa bắt đầu chuẩn bị)
3 câu hỏi. Giấy bút. 10 phút. Bạn có một bản đồ state hoàn chỉnh mà AI có thể build chính xác.
State không chỉ cho đơn hàng
State có ở khắp nơi trong app. Mỗi tính năng đều có state riêng.
User account: Chưa xác nhận email, Active, Bị khóa, Đã xóa
Tin nhắn: Đang gửi, Đã gửi, Đã nhận, Đã đọc
Form nhập liệu: Trống, Đang nhập, Hợp lệ, Lỗi
Nút bấm: Bình thường, Đang loading, Thành công, Lỗi, Disabled
Trang web: Đang tải, Đã tải, Lỗi, Trống (không có dữ liệu)
Subscription: Dùng thử, Active, Sắp hết hạn, Hết hạn, Đã hủy
Bạn thấy không? Ngay cả một cái nút bấm cũng có 5 state. Nếu bạn không nói cho AI biết nút có những state nào, AI sẽ chỉ làm 2 state: bình thường và đã bấm. Thiếu loading, thiếu xử lý lỗi, thiếu disabled khi đang xử lý. Và người dùng bấm 5 lần liên tục, tạo 5 đơn hàng.
Template Prompt: Copy-paste ngay
Trước khi nhờ AI build tính năng, dùng prompt này để liệt kê state:
Tôi muốn build tính năng [mô tả tính năng].
Trước khi code, hãy giúp tôi liệt kê tất cả state (trạng thái) có thể có.
Với mỗi state, cho tôi biết:
Tên state
Giao diện hiển thị gì ở state này
Từ state này có thể chuyển sang state nào
Điều gì kích hoạt việc chuyển state
Đừng quên state lỗi, state loading, và state hủy. Chưa cần code, chỉ cần danh sách state.
Sau khi có danh sách state, dùng prompt tiếp:
Dựa trên danh sách state ở trên, hãy build tính năng này.
Yêu cầu:
Mỗi state phải có giao diện tương ứng
Chỉ cho phép chuyển state theo đúng quy tắc đã liệt kê
Xử lý tất cả state lỗi và state loading
Không cho phép user thực hiện action khi đang ở state không phù hợp
Trước và sau khi áp dụng State Thinking
TRƯỚC:
Prompt: "Tạo hệ thống đặt hàng online." Kết quả: AI tạo 1 form đặt hàng và 1 trang hiển thị đơn. Đơn hàng chỉ có 2 trạng thái: "mới" và "xong". Không có loading khi thanh toán, khách bấm 3 lần tạo 3 đơn. Không có trạng thái hủy. Không phân biệt "đang giao" và "đã giao".
SAU:
Prompt: "Tạo hệ thống đặt hàng với các state sau:
Chờ xác nhận: hiện 'Đơn hàng đang chờ shop xác nhận'. Khách có thể hủy.
Đã xác nhận: hiện 'Shop đã xác nhận, đang chuẩn bị'. Khách không thể hủy nữa.
Đang giao: hiện 'Shipper đang giao hàng' + tracking info.
Đã giao: hiện 'Đã giao thành công'. Cho phép đánh giá.
Đã hủy: hiện 'Đơn đã hủy' + lý do.
Hoàn tiền: hiện 'Đang xử lý hoàn tiền'.
Nút thanh toán: khi bấm chuyển sang state loading, disable nút, hiện spinner. Chỉ chuyển sang 'Chờ xác nhận' khi payment thành công. Nếu lỗi, hiện thông báo, cho bấm lại."
Kết quả: Hệ thống hoàn chỉnh. Không bị trừ tiền 2 lần. Mỗi đơn hàng hiển thị đúng trạng thái. Khách biết đơn đang ở đâu.
Kết hợp với 4 bài trước: Flow hoàn chỉnh
5 bài rồi. Bạn đã có 5 công cụ tư duy:
Bước 1 (Decomposition, bài 1): Chia ý tưởng thành các phần nhỏ Bước 2 (Pattern Recognition, bài 2): Nhận diện mỗi phần thuộc pattern gì Bước 3 (Abstraction, bài 3): Chọn level chi tiết phù hợp Bước 4 (Algorithm Thinking, bài 4): Vẽ flow từng bước + điều kiện Bước 5 (State Thinking, bài 5): Liệt kê tất cả state + quy tắc chuyển state
Ví dụ: "App quản lý đơn hàng cho quán trà sữa"
Decomposition: Đăng nhập, menu, đặt hàng, thanh toán, theo dõi đơn, đánh giá Pattern Recognition: Auth, CRUD (menu), Booking (đặt hàng), Payment, Notification Abstraction: Bắt đầu với trang đặt hàng + theo dõi đơn Algorithm Thinking: Flow đặt hàng: chọn món, thêm topping, chọn size, xác nhận, thanh toán State Thinking: Đơn hàng có 6 state: chờ xác nhận, đang pha chế, sẵn sàng lấy, đã lấy, đã hủy, hoàn tiền
Mỗi bài thêm 1 lớp tư duy. Xếp chồng lên nhau. Prompt cho AI càng ngày càng chính xác.
Bài tập thực hành (15 phút)
Chọn 1 trong các tính năng sau (hoặc dùng app bạn đang build):
Giỏ hàng (shopping cart)
Tài khoản người dùng
Tin nhắn / chat
Làm theo 3 bước:
Bước 1 (5 phút): Liệt kê TẤT CẢ state có thể có. Đừng quên state lỗi, state loading, state "trống" (empty state).
Bước 2 (5 phút): Với mỗi state, viết ra: từ state này chuyển sang state nào được? Điều gì trigger việc chuyển?
Bước 3 (5 phút): Viết 1 prompt cho AI, mô tả tính năng kèm danh sách state. Dùng template ở trên.
Bài tiếp theo: MVP Thinking - "Đừng build cái hoàn hảo, build cái chạy được." Bạn mất 3 tuần vibe code 1 app với 20 tính năng. Cuối cùng không ai dùng vì điều duy nhất họ cần là search bar.