Debugging: Finding It Yourself
Một hướng dẫn thực tế về debugging mà không cần hỏi người khác trước — cách cô lập, xác minh và thực sự hiểu được các lỗi của bạn.
Hầu hết người mới bắt đầu coi một bug như một bức tường. Họ đâm vào nó, dừng lại, và hỏi người khác leo qua nó giúp. Thói quen thực sự xây dựng kỹ năng là khác: bạn học cách coi một bug như một câu hỏi có câu trả lời có thể tìm được, và bạn tìm nó trước khi hỏi.
Đọc thông báo lỗi như nó là bằng chứng, không phải tiếng ồn
Stack traces thường bị bỏ qua vì chúng trông kinh khủng. Nhưng chúng thường nói cho bạn biết chính xác nơi và tại sao có gì đó bị hỏng. Nếu bạn nhận được TypeError: 'NoneType' object is not subscriptable trong Python, đó không phải là vô nghĩa ngẫu nhiên — nó có nghĩa là một biến mà bạn mong đợi chứa một list hoặc dict thực sự là None. Số dòng cho bạn biết nơi đó. Công việc của bạn là truy ngược từ dòng đó và tìm nơi giá trị seharusnya được đặt nhưng không được.
Làm điều này trước khi tìm kiếm bất kỳ thứ gì trực tuyến: đọc dòng cuối cùng của traceback trước, sau đó làm việc lên. Dòng cuối cùng thường đặt tên cho ngoại lệ thực tế. Các dòng ở trên nó hiển thị chuỗi cuộc gọi đưa bạn tới đó.
Tái tạo lại nó một cách cố ý
Nếu một bug chỉ xuất hiện đôi khi, bạn chưa hiểu nó. Trước khi chạm vào code, hãy cố gắng làm cho nó xảy ra một cách đáng tin cậy. Thay đổi một đầu vào tại một thời điểm. Nó có thất bại với một danh sách trống nhưng không phải với danh sách đầy đủ không? Nó có chỉ thất bại khi lần gọi thứ hai của một hàm, không phải lần đầu tiên không? Một bug mà bạn có thể tái tạo lại theo lệnh là một bug được giải quyết 80%, bởi vì bây giờ bạn có thể kiểm tra xem liệu một fix có thực sự hoạt động hay là bạn chỉ đoán.
Cắt vấn đề làm đôi, sau đó lại làm đôi
Binary search không chỉ dành cho các mảng được sắp xếp — nó là cách nhanh nhất để tìm code bị hỏng trong một hàm dài hoặc pipeline. Comment out hoặc bypass nửa thứ hai của logic của bạn và kiểm tra xem nửa thứ nhất có vẫn tạo ra bug không. Nếu có, vấn đề nằm ở nửa thứ nhất. Nếu không, nó nằm ở nửa thứ hai. Lặp lại. Điều này hoạt động cho một script 200 dòng cũng như hoạt động cho một multi-stage data pipeline nơi bạn kiểm tra các đầu ra ở mỗi giai đoạn với print() hoặc df.head().
Điều này tốt hơn việc ngẫu nhiên thay đổi các dòng và chạy lại, đó là điều hầu hết mọi người làm dưới áp lực và nó lãng phí nhiều thời gian hơn so với tiết kiệm.
Sử dụng debugger thay vì print() khi print() dừng hoạt động
Các câu lệnh print() là tốt cho các trường hợp đơn giản, nhưng khi bạn đang theo dõi trạng thái trên nhiều lệnh gọi hàm, một debugger thực sự tiết kiệm thời gian thực. Trong Python, đặt import pdb; pdb.set_trace() ngay trước dòng đáng ngờ, chạy script, và bạn sẽ nhận được một prompt trực tiếp nơi bạn có thể kiểm tra các biến, bước từng dòng với n, và bước vào các cuộc gọi hàm với s. Debugger tích hợp của VS Code làm điều tương tự với các breakpoints mà bạn nhấp chuột thay vì gõ. Dù bằng cách nào, mục tiêu là giống nhau: quan sát trạng thái thực tế của chương trình của bạn thay vì đoán nó có lẽ là gì.
Cô lập nó khỏi phần còn lại của dự án của bạn
Nếu một bug xảy ra bên trong một codebase lớn, đừng debug nó bên trong codebase lớn. Sao chép hàm liên quan vào một tập tin mới với đầu vào giả, tối thiểu kích hoạt cùng một lỗi. Nếu bug biến mất, có gì đó về bối cảnh xung quanh — một biến global, một import, trạng thái cũ — là nguyên nhân thực sự, và bạn vừa học được điều gì đó quan trọng. Nếu bug vẫn tồn tại khi cô lập, bây giờ bạn có một trường hợp nhỏ, có thể chia sẻ, có thể kiểm tra, và bạn gần hơn nhiều đến fix thực tế.
Kiểm tra các giả định của bạn so với thực tế, không phải trí nhớ
Một phần lớn thời gian debugging được dùng cho các giả định không được đặt câu hỏi:
Viết với hỗ trợ của AI, được xem xét và đăng bởi Michal Pilch (CISSP), Korra Studio.
Đây là một ghi chép từ cơ sở kiến thức Korra Studio — nền tảng kết hợp mỗi chủ đề với phiên hỗ trợ 1-kèm-1.
Bắt đầu miễn phíarrow_forward