Gỡ lỗi các báo cáo lỗi bạn không viết bằng quy trình thực tế để tái hiện lỗi, cô lập UI/API/DB, và yêu cầu bản vá AI tối thiểu có thể kiểm tra.

Gỡ lỗi một báo cáo lỗi bạn không viết khó hơn vì bạn thiếu bản đồ tư duy của người xây dựng ban đầu. Bạn không biết chỗ nào dễ vỡ, điều gì là "bình thường", hoặc những shortcut nào đã được dùng. Một triệu chứng nhỏ (một nút, một lỗi chính tả, một màn hình chậm) vẫn có thể bắt nguồn từ vấn đề sâu hơn ở API, cơ sở dữ liệu, hoặc một job nền.
Một báo cáo hữu ích cho bạn bốn thứ:
Hầu hết báo cáo chỉ cho bạn phần cuối: "Không lưu được", "hỏng", "lỗi ngẫu nhiên." Thiếu đi bối cảnh khiến vấn đề có thể lặp lại: vai trò người dùng, bản ghi cụ thể, môi trường (prod vs staging), và liệu lỗi có bắt đầu sau một thay đổi nào đó không.
Mục tiêu là biến một triệu chứng mơ hồ thành một cách tái hiện đáng tin cậy. Khi bạn có thể làm nó xảy ra theo yêu cầu, nó không còn bí ẩn nữa. Nó trở thành một chuỗi kiểm tra.
Những gì bạn có thể kiểm soát ngay lập tức:
"Xong" không phải là "Tôi nghĩ đã sửa." Xong là: các bước tái hiện của bạn pass sau một thay đổi nhỏ, và bạn nhanh chóng kiểm tra lại các hành vi lân cận có thể bị ảnh hưởng.
Cách nhanh nhất để lãng phí thời gian là thay đổi nhiều thứ cùng lúc. Khoá điểm bắt đầu của bạn để mỗi kết quả thử nghiệm có ý nghĩa.
Chọn một môi trường và gắn bó với nó cho đến khi bạn có thể tái hiện vấn đề. Nếu báo cáo đến từ production, xác nhận ở đó trước. Nếu quá rủi ro, dùng staging. Local cũng ổn nếu bạn có thể khớp sát dữ liệu và cấu hình.
Rồi xác định mã nào đang chạy thực tế: phiên bản, thời điểm build, và bất kỳ feature flags hoặc config nào ảnh hưởng luồng. Những khác biệt nhỏ (tích hợp bị tắt, base URL API khác, job nền không chạy) có thể biến một lỗi thật thành ma.
Tạo một thiết lập thử nghiệm sạch và có thể lặp lại. Dùng một tài khoản mới và dữ liệu đã biết. Nếu có thể, reset trạng thái trước mỗi lần thử (đăng xuất, xóa cache, bắt đầu từ cùng một bản ghi).
Ghi lại các giả định khi bạn đi. Đây không phải việc làm chiếm thời gian; nó ngăn bạn tranh luận với chính mình sau này.
Mẫu ghi chú baseline:
Nếu không tái hiện được, những ghi chú này cho bạn biết cần thay gì tiếp theo, từng nút một.
Chiến thắng nhanh nhất là biến một lời phàn nàn mơ hồ thành thứ bạn có thể chạy như một script.
Bắt đầu bằng cách viết lại báo cáo như một user story ngắn: ai đang làm gì, ở đâu, và họ mong đợi điều gì. Rồi thêm kết quả quan sát được.
Ví dụ viết lại:
"Là một billing admin, khi tôi đổi trạng thái invoice thành Paid và nhấn Save trên trang hóa đơn, trạng thái nên được lưu. Thay vào đó, trang vẫn như cũ và trạng thái không thay đổi sau khi refresh."
Tiếp theo, nắm bắt các điều kiện khiến báo cáo đúng. Lỗi thường xoay quanh một chi tiết thiếu: vai trò, trạng thái bản ghi, locale, hoặc môi trường.
Các đầu vào chính cần ghi trước khi bạn click thử:
Thu thập bằng chứng khi hành vi gốc còn rõ. Ảnh chụp màn hình hữu ích, nhưng một đoạn quay ngắn còn tốt hơn vì nó ghi lại thời điểm và các click chính xác. Luôn ghi timestamp (kèm timezone) để bạn có thể đối chiếu log sau này.
Ba câu hỏi làm rõ loại bỏ phần lớn sự đoán:
Đừng bắt đầu bằng đoán nguyên nhân. Làm cho vấn đề xảy ra có chủ ý, theo cùng một cách, hơn một lần.
Đầu tiên, chạy đúng các bước người báo cáo mô tả. Đừng "cải thiện" chúng. Ghi lại điểm đầu tiên trải nghiệm của bạn khác, dù là nhỏ (nhãn nút khác, trường bị thiếu, văn bản lỗi hơi khác). Khớp sai đầu tiên đó thường là manh mối.
Một workflow đơn giản áp dụng cho hầu hết app:
Sau khi nó lặp lại được, thay đổi từng thứ một. Các test đơn biến thường mang lại kết quả:
Kết thúc bằng một script repro ngắn người khác có thể chạy trong 2 phút: trạng thái khởi đầu, các bước, các đầu vào, và quan sát thất bại đầu tiên.
Trước khi đọc toàn bộ codebase, quyết định lớp nào đang lỗi.
Hỏi: triệu chứng chỉ ở UI hay có trong dữ liệu và phản hồi API nữa?
Ví dụ: "Tên hồ sơ của tôi không cập nhật." Nếu API trả tên mới nhưng UI vẫn hiển thị tên cũ, nghi ngờ trạng thái UI/cache. Nếu API không lưu, nhiều khả năng lỗi ở API hoặc DB.
Câu hỏi phân loại nhanh có thể trả lời trong vài phút:
Kiểm tra UI xoay quanh hiển thị: lỗi console, tab Network, và trạng thái cũ (UI không fetch lại sau khi lưu, hoặc đọc từ cache cũ).
Kiểm tra API liên quan đến hợp đồng: payload (các trường, kiểu, ID), mã trạng thái, và body lỗi. Một 200 với body bất ngờ có thể quan trọng như một 400.
Kiểm tra DB là về thực tế: hàng bị thiếu, ghi không đầy đủ, vi phạm ràng buộc, cập nhật chạm 0 hàng vì WHERE không khớp.
Để giữ phương hướng, phác thảo một bản đồ nhỏ: hành động UI nào gọi endpoint nào, và endpoint đó đọc hoặc ghi bảng nào.
Sự rõ ràng thường đến khi bạn theo dõi một request thực sự từ click tới database và ngược lại.
Bắt ba mốc từ báo cáo hoặc repro của bạn:
Nếu bạn không có correlation ID, thêm một cái ở gateway/backend và bao gồm nó trong header phản hồi và logs.
Để tránh bị ngập trong tiếng ồn, chỉ lấy những gì cần để trả lời "Nó thất bại ở đâu và vì sao?":
Các tín hiệu cần chú ý:
Nếu "hôm qua chạy được mà hôm nay không", nghi ngờ drift môi trường: flags thay đổi, secret xoay vòng, migration thiếu, hoặc job dừng hoạt động.
Lỗi dễ sửa nhất là thử nghiệm nhỏ, có thể lặp lại.
Thu nhỏ mọi thứ: ít click hơn, ít trường hơn, dataset nhỏ nhất vẫn gây lỗi. Nếu chỉ xảy ra với "khách hàng có nhiều bản ghi", thử tạo một trường hợp tối thiểu vẫn kích hoạt nó. Nếu không thể, đó là manh mối lỗi liên quan khối lượng dữ liệu.
Tách "trạng thái xấu" khỏi "mã xấu" bằng cách reset trạng thái có chủ ý: tài khoản sạch, tenant/dataset mới, build đã biết.
Một cách thực tế giữ repro rõ ràng là bảng input gọn:
| Given (setup) | When (action) | Expect | Got |
|---|---|---|---|
| User role: Editor; one record with Status=Draft | Click Save | Toast "Saved" + updated timestamp | Button shows spinner then stops; no change |
Làm cho repro có thể di chuyển để người khác chạy nhanh:
Con đường nhanh nhất thường nhàm chán: thay đổi từng thứ một, quan sát, ghi chú.
Những sai lầm phổ biến:
Ví dụ thực tế: ticket ghi "Export CSV trắng." Bạn test bằng tài khoản admin và thấy dữ liệu. Người dùng có role giới hạn, và API trả danh sách rỗng vì filter quyền. Nếu bạn chỉ vá UI để hiện "No rows," bạn bỏ qua câu hỏi thực sự: role đó có được phép export hay product nên giải thích vì sao bị lọc?
Sau bất kỳ bản vá nào, chạy lại đúng các bước repro, rồi thử một kịch bản lân cận vẫn nên hoạt động.
Bạn sẽ nhận được câu trả lời tốt hơn từ đồng nghiệp (hoặc công cụ) nếu bạn mang theo một gói gọn: các bước có thể lặp lại, một lớp khả nghi, và bằng chứng.
Trước khi ai đó thay đổi code, xác nhận:
Rồi làm một lượt kiểm tra hồi qui nhanh: thử vai trò khác, một trình duyệt cửa sổ riêng/ẩn danh, một tính năng lân cận dùng cùng endpoint/bảng, và một input góc (rỗng, văn bản dài, ký tự đặc biệt).
Một tin nhắn support nói: "Nút Save không làm gì trên form Edit Customer." Theo dõi cho biết chỉ xảy ra với khách hàng tạo trước tháng trước, và chỉ khi bạn thay email thanh toán.
Bắt đầu ở UI và giả định lỗi đơn giản nhất trước. Mở bản ghi, sửa, và tìm dấu hiệu rằng "không có gì" thực ra có thể là "có thứ gì đó": nút bị disabled, toast ẩn, thông báo xác thực không hiển thị. Mở console trình duyệt và tab Network.
Ở đây, khi bấm Save gửi một request, nhưng UI không hiển thị kết quả vì frontend chỉ coi 200 là thành công và bỏ qua lỗi 400. Tab Network hiển thị response 400 với body JSON như: {"error":"billingEmail must be unique"}.
Bây giờ xác thực API thật sự fail: lấy payload chính xác từ request và replay nó. Nếu nó cũng fail ngoài UI, đừng tiếp tục theo đuổi lỗi trạng thái frontend.
Rồi kiểm tra database: tại sao uniqueness chỉ fail với các bản ghi cũ? Bạn phát hiện các khách hàng legacy chia sẻ một billing_email placeholder từ nhiều năm trước. Một kiểm tra unique mới chặn lưu bất kỳ khách hàng nào vẫn dùng placeholder đó.
Minimal repro bạn có thể chuyển giao:
billing_email = [email protected].billingEmail must be unique.Tiêu chí chấp nhận: khi API trả lỗi xác thực, UI hiện thông báo lỗi, giữ lại các sửa đổi của người dùng, và lỗi chỉ ra đúng trường bị lỗi.
Khi lỗi đã tái hiện và bạn đã xác định lớp khả nghi, hãy yêu cầu trợ giúp sao cho tạo ra một bản vá nhỏ, an toàn.
Đóng gói một "case file" đơn giản: các bước repro tối thiểu (với inputs, môi trường, role), mong đợi vs thực tế, lý do bạn cho là UI/API/DB, và đoạn log nhỏ nhất cho thấy lỗi.
Rồi làm yêu cầu hẹp:
Nếu bạn dùng nền tảng vibe-coding như Koder.ai (koder.ai), cách làm case-file này giữ đề xuất tập trung. Snapshot và rollback của nó cũng giúp bạn thử các thay đổi nhỏ an toàn và quay lại baseline đã biết.
Bàn giao cho một developer có kinh nghiệm khi sửa ảnh hưởng tới bảo mật, thanh toán, migration dữ liệu, hoặc bất cứ gì có thể làm hỏng dữ liệu production. Cũng bàn giao nếu thay đổi ngày càng lớn hơn một patch nhỏ hoặc bạn không thể giải thích rủi ro bằng lời đơn giản.
Bắt đầu bằng cách viết lại nó thành một kịch bản có thể tái hiện: ai (vai trò), ở đâu (trang/quy trình), các dữ liệu đầu vào chính xác (ID, bộ lọc, payload), bạn mong đợi gì và bạn thấy gì. Nếu thiếu phần nào, hãy hỏi để lấy một tài khoản ví dụ và một ID bản ghi ví dụ để bạn có thể chạy cùng một kịch bản đầu-cuối.
Chọn một môi trường và giữ nguyên nó cho đến khi bạn tái hiện được. Sau đó ghi lại build/phiên bản, feature flags, cấu hình, tài khoản/role thử nghiệm và dữ liệu chính xác bạn đã dùng. Điều này ngăn bạn “sửa” một lỗi chỉ vì thiết lập của bạn khác với người báo cáo.
Làm cho lỗi xuất hiện hai lần bằng cùng các bước và dữ liệu, rồi loại bỏ mọi thứ không cần thiết. Mục tiêu là 3–6 bước từ trạng thái bắt đầu sạch với một bản ghi hoặc body request có thể tái sử dụng. Nếu không thể thu gọn, đó thường là dấu hiệu liên quan đến khối lượng dữ liệu, thời gian hoặc job nền.
Đừng thay đổi gì vội. Chạy đúng các bước người báo cáo ghi, và ghi lại chỗ đầu tiên mà trải nghiệm của bạn khác (nhãn nút khác, trường thiếu, thông báo lỗi khác). Sự sai lệch đầu tiên đó thường là manh mối cho điều kiện thực sự gây lỗi.
Kiểm tra xem dữ liệu có thực sự thay đổi không. Nếu API trả về giá trị mới nhưng UI vẫn hiển thị giá trị cũ thì nhiều khả năng là trạng thái UI, cache hoặc việc refetch. Nếu API trả sai hoặc thao tác lưu không xảy ra, hãy tập trung vào API hoặc DB. Nếu bản ghi DB không cập nhật (hoặc cập nhật 0 hàng), vấn đề nằm ở lớp lưu trữ hoặc điều kiện truy vấn.
Xác nhận có một request mạng khi bạn bấm nút, rồi kiểm tra payload của request và body của response, không chỉ mã trạng thái. Ghi lại dấu thời gian (kèm timezone) và định danh người dùng để khớp với log backend. Một 200 “thành công” nhưng với body bất ngờ có thể quan trọng như một 400/500.
Thay đổi từng yếu tố một: vai trò, bản ghi (mới vs legacy), trình duyệt/thiết bị, phiên sạch (incognito/xóa cache), và mạng. Thử từng biến đơn lẻ sẽ cho bạn biết điều kiện nào quan trọng và tránh việc theo đuổi những trùng hợp ngẫu nhiên do thay nhiều thứ cùng lúc.
Thay đổi nhiều biến cùng lúc, test trên môi trường khác với người báo cáo, và bỏ qua vai trò/permission là những điều lãng phí thời gian nhất. Sai lầm phổ biến khác là sửa bề mặt UI trong khi lỗi thực sự nằm ở API/DB. Sau khi thay đổi, luôn chạy lại kịch bản gốc rồi thử một trường hợp lân cận.
“Hoàn thành” nghĩa là: bản sao nhỏ gốc giờ chạy thành công, và bạn đã kiểm tra lại một luồng gần kề có thể bị ảnh hưởng. Giữ kiểm tra cụ thể, như một tín hiệu thành công hiển thị, HTTP response đúng, hoặc thay đổi bản ghi DB mong đợi. Tránh nói “tôi nghĩ đã sửa xong” nếu không chạy lại cùng các đầu vào trên cùng baseline.
Đưa một case file chặt chẽ: các bước tối thiểu với dữ liệu chính xác, môi trường/build/flags, tài khoản thử nghiệm và role, mong đợi vs thực tế, và một bằng chứng (request/response, văn bản lỗi, hoặc đoạn log có timestamp). Rồi yêu cầu bản vá nhỏ nhất để làm cho repro pass và kèm theo kế hoạch kiểm thử ngắn.