Bỏ qua đến nội dung

Chương 6: Debug & Xác Minh

Debug không phải là đoán mò. Đó là một quá trình có hệ thống, dựa trên bằng chứng. Superpowers thực thi một cách tiếp cận có cấu trúc để debug loại bỏ các nguyên nhân thất bại phổ biến nhất: các lần thử sửa ngẫu nhiên, xác minh không đầy đủ, và các tuyên bố hoàn thành sớm.


Quy Trình Debug 4 Giai Đoạn

Mọi cuộc điều tra bug phải tuân theo bốn giai đoạn này theo thứ tự. Không được phép bỏ qua một giai đoạn.

Giai Đoạn 1: Điều Tra Nguyên Nhân Gốc

Trước khi viết một dòng code sửa lỗi, bạn phải hiểu đầy đủ vấn đề.

  1. Đọc lỗi cẩn thận. Sao chép toàn bộ thông báo lỗi, stack trace, và bất kỳ context xung quanh. Đừng diễn giải lại — làm việc với văn bản chính xác.
  2. Tái hiện lỗi. Chạy test thất bại hoặc kích hoạt bug thủ công. Một bug bạn không thể tái hiện là bug bạn không thể sửa đáng tin cậy.
  3. Kiểm tra thay đổi gần đây. Chạy git log --oneline -20git diff HEAD~5 để xác định những gì đã thay đổi gần đây. Hầu hết các bug được đưa vào bởi các thay đổi gần đây.
  4. Theo dõi luồng dữ liệu. Theo dõi dữ liệu từ điểm vào của nó qua mọi biến đổi cho đến khi bạn tìm thấy nơi nó khác với giá trị mong đợi.

Quy tắc: Đừng đưa ra giả thuyết cho đến khi bạn đã hoàn thành giai đoạn điều tra. Bằng chứng trước lý thuyết.


Giai Đoạn 2: Phân Tích Mô Hình

Khi bạn đã tái hiện bug, hãy tìm kiếm các mô hình.

  1. Tìm các ví dụ hoạt động. Xác định vị trí các code path tương tự trong codebase hoạt động đúng. Sự khác biệt giữa code hoạt động và code bị hỏng có thể là bug.
  2. So sánh sự khác biệt. Sử dụng một diff có hệ thống — types, nullability, xử lý async, lan truyền lỗi, side effects.
  3. Kiểm tra timing. Nhiều bug là race condition, vấn đề thứ tự khởi tạo, hoặc lỗi async/await. Hỏi: cái này có hoạt động đồng bộ nhưng thất bại không đồng bộ? Cái này có hoạt động lần đầu nhưng thất bại khi retry?

Giai Đoạn 3: Giả Thuyết và Kiểm Tra

Với bằng chứng đã thu thập, bạn có thể hình thành và kiểm tra một giả thuyết.

  1. Phát biểu giả thuyết của bạn một cách rõ ràng. Viết nó ra: "Tôi tin rằng bug được gây ra bởi X vì bằng chứng Y."
  2. Kiểm tra một biến tại một thời điểm. Không bao giờ thay đổi hai thứ cùng một lúc. Nếu cách sửa của bạn không hoạt động, bạn sẽ không biết thay đổi nào chịu trách nhiệm.
  3. Ghi chép kết quả của bạn. Ghi lại những gì bạn đã kiểm tra, những gì bạn mong đợi, và những gì thực sự đã xảy ra. Nhật ký này ngăn bạn kiểm tra lại cùng một giả thuyết sai.

Giai Đoạn 4: Thực Hiện

Bạn đã biết nguyên nhân gốc. Hãy thực hiện sửa lỗi đúng cách.

  1. Tạo một test thất bại trước. Viết một test tái hiện bug chính xác. Nó phải thất bại trước khi sửa và đạt sau khi sửa.
  2. Thực hiện sửa lỗi tối thiểu. Chỉ thay đổi những gì cần thiết để sửa nguyên nhân gốc đã xác định. Đừng refactor, đừng cải thiện code không liên quan.
  3. Xác minh không có regression. Chạy toàn bộ bộ test. Cách sửa không được phá vỡ bất cứ điều gì đã đạt trước đó.

Quy Tắc 3 Lần Thất Bại

Nếu bạn đã thực hiện 3 lần sửa trở lên và bug vẫn còn, DỪNG LẠI NGAY.

Ba lần sửa thất bại không phải là dấu hiệu để cố gắng hơn — đó là tín hiệu chẩn đoán. Có nghĩa là bạn đang xử lý triệu chứng, không phải nguyên nhân gốc. Tại thời điểm này:

  • Vấn đề gần như chắc chắn là vấn đề kiến trúc, không phải bug ở cấp độ dòng
  • Tiếp tục cố gắng sửa sẽ làm cho codebase tệ hơn
  • Bạn phải báo cáo: viết một kế hoạch, thiết kế lại module bị ảnh hưởng, hoặc tìm kiếm góc nhìn thứ hai

Quy tắc này ngăn mô hình thất bại phổ biến "sửa-lại-sửa" tích lũy nợ kỹ thuật và làm cho codebase không thể bảo trì.


Quy Tắc Một Lần Sửa

Không bao giờ sửa nhiều thứ cùng một lúc.

Khi bạn gặp một bug, bạn có thể chú ý đến các vấn đề khác ở gần đó. Hãy cưỡng lại sự thôi thúc sửa tất cả chúng cùng một lúc. Tại sao?

  • Bạn không thể xác định thay đổi nào đã sửa bug gốc
  • Bạn không thể quy một lỗi mới cho một thay đổi cụ thể
  • Coverage test của bạn trở nên không rõ ràng
  • Code review trở nên không thể thực hiện

Sửa một thứ. Xác minh nó. Commit nó. Sau đó chuyển sang vấn đề tiếp theo.


Xác Minh Trước Khi Hoàn Thành

Đây là một trong những Luật Sắt của Superpowers:

"KHÔNG CÓ TUYÊN BỐ HOÀN THÀNH MÀ KHÔNG CÓ BẰNG CHỨNG XÁC MINH MỚI"

Bạn không được nói "đã sửa", "xong", "đang hoạt động", hoặc bất kỳ cụm từ tương đương nào trừ khi bạn vừa chạy xác minh và có output trước mặt bạn. Điều này không phải tùy chọn.

Cổng Xác Minh 5 Bước

Mọi tuyên bố hoàn thành phải đi qua cổng này:

BướcHành ĐộngÝ Nghĩa
XÁC ĐỊNHĐặt tên cho thứ cụ thể bạn đang xác minh"Tôi đang xác minh rằng luồng đăng nhập đạt tất cả các test"
CHẠYThực thi lệnh xác minh thực tếnpm test -- --testPathPattern=auth
ĐỌCĐọc toàn bộ outputĐừng lướt. Đọc mỗi dòng.
XÁC NHẬNXác nhận output khớp với tiêu chí thành côngTất cả test đạt, không có cảnh báo, hành vi mong đợi được quan sát
TUYÊN BỐChỉ bây giờ mới nói rằng công việc hoàn chỉnh"Test đạt. Cách sửa đã được xác minh."

Nếu bất kỳ bước nào thất bại, bạn quay lại quy trình debug. Bạn không tuyên bố hoàn thành.


Các Dấu Hiệu Đỏ Cần Chú Ý

Các cụm từ và hành vi này cho thấy xác minh đã bị bỏ qua:

Dấu Hiệu ĐỏTín Hiệu
"Cái này sẽ hoạt động"Không có xác minh nào được thực hiện
"Có lẽ ổn"Giả định không có bằng chứng
"Tôi nghĩ đã sửa"Niềm tin chủ quan, không phải xác minh khách quan
"Các test sẽ đạt"Test chưa được chạy
Tin tưởng vào báo cáo trước đó mà không chạy lạiDữ liệu cũ — mọi thứ thay đổi
Tóm tắt output thay vì hiển thị nóCó thể đọc có chọn lọc hoặc ảo giác
Sửa test thay vì codeChe giấu bug, không giải quyết nó

Khi bạn bắt gặp mình sắp nói bất kỳ cụm từ nào trong số này, hãy dừng lại. Chạy xác minh. Sau đó báo cáo những gì output thực sự nói.


Kết Hợp Tất Cả Lại

Một phiên debug đúng trông như thế này:

1. Bug được báo cáo: "Đăng nhập người dùng thất bại với 401"
2. Giai đoạn 1: Tái hiện → kiểm tra git log → theo dõi auth middleware
3. Giai đoạn 2: Tìm endpoint hoạt động → so sánh headers → xác định: JWT token thiếu tiền tố "Bearer " trong một code path
4. Giai đoạn 3: Giả thuyết: "Client mobile gửi raw token, desktop gửi 'Bearer TOKEN'. Backend mong đợi 'Bearer'."
   Kiểm tra: Gửi raw token thủ công → 401. Gửi với tiền tố "Bearer" → 200. Đã xác nhận.
5. Giai đoạn 4: Viết test cho raw token input → implement normalization trong auth middleware → chạy toàn bộ bộ test → 847 test đạt, 0 thất bại
6. Cổng xác minh: XÁC ĐỊNH "auth tests" → CHẠY `npm test` → ĐỌC output → XÁC NHẬN 0 thất bại → TUYÊN BỐ "Đã sửa."

Debug có hệ thống chậm hơn trong ngắn hạn và nhanh hơn đáng kể trong suốt vòng đời của dự án. Đoán mò là kẻ thù của phần mềm đáng tin cậy.