0

Cách Debug nhanh hơn với 1 số mẹo hữu ích

Debug (gỡ lỗi) là một phần thiết yếu của quy trình phát triển phần mềm. Cho dù bạn có kinh nghiệm đến đâu, bạn cũng sẽ không tránh khỏi việc gặp phải lỗi trong mã của mình. Chìa khóa để trở thành một nhà phát triển năng suất không chỉ là viết mã nhanh mà còn phải gỡ lỗi hiệu quả.

Trong nhiều năm qua, tôi đã phát triển một bộ thủ thuật đơn giản giúp tôi gỡ lỗi nhanh hơn và hiệu quả hơn. Trong bài đăng này, tôi sẽ chia sẻ những thủ thuật này với bạn, cùng với các ví dụ về mã để minh họa cách chúng hoạt động.

Hãy cùng bắt đầu nhé!

1. Hiểu rõ vấn đề trước khi bắt đầu gỡ lỗi

Trước khi bắt đầu gỡ lỗi, điều quan trọng là phải hiểu vấn đề bạn đang cố gắng giải quyết. Điều này có vẻ hiển nhiên, nhưng nhiều lập trình viên thường nhảy thẳng vào gỡ lỗi mã mà không hiểu đầy đủ vấn đề. Vậy nên bạn hãy dành thời gian để:

  • Tái tạo lỗi: Đảm bảo bạn có thể tái tạo lỗi một cách nhất quán. Nếu bạn không thể tái tạo lỗi, sẽ khó sửa hơn nhiều.
  • Hiểu hành vi mong đợi: Biết mã được cho là sẽ làm gì. Điều này sẽ giúp bạn xác định được nơi nào đang xảy ra sai sót.
  • Thu thập thông tin: Thu thập mọi thông báo lỗi, nhật ký hoặc báo cáo của người dùng có thể cung cấp cho bạn manh mối về sự cố.

Ví dụ: Giả sử bạn có một hàm được cho là tính tổng của một mảng số, nhưng nó lại trả về kết quả sai.

def calculate_sum(numbers):
    total = 0
    for number in numbers:
        total += number
    return total

# Test case
result = calculate_sum([1, 2, 3, 4])
print(result)  # Expected output: 10, but getting 9

Trước khi tìm hiểu mã, hãy đảm bảo rằng bạn hiểu được hành vi mong đợi (tổng [1, 2, 3, 4] phải là 10) và bạn có thể tái tạo sự cố một cách nhất quán.

2. Sử dụng các câu lệnh in một cách chiến lược

Câu lệnh in là một trong những công cụ gỡ lỗi đơn giản và hiệu quả nhất. Bằng cách đặt câu lệnh in một cách chiến lược trong mã của bạn, bạn có thể theo dõi luồng thực thi và kiểm tra giá trị của các biến tại các điểm khác nhau.

Ví dụ: Hãy thêm một số câu lệnh in vào hàm calculate_sum của chúng ta để xem có vấn đề gì không.

def calculate_sum(numbers):
    total = 0
    for number in numbers:
        print(f"Adding {number} to total ({total})")
        total += number
    return total

# Test case
result = calculate_sum([1, 2, 3, 4])
print(result)  # Expected output: 10, but getting 9

Khi bạn chạy mã này, bạn sẽ thấy kết quả sau:

Adding 1 to total (0)
Adding 2 to total (1)
Adding 3 to total (3)
Adding 4 to total (6)
9

Từ kết quả đầu ra, bạn có thể thấy tổng số không được cập nhật chính xác sau lần lặp đầu tiên. Điều này cho bạn manh mối rằng có điều gì đó không ổn với cách total đang được tăng dần.

3. Sử dụng trình gỡ lỗi

Mặc dù các câu lệnh in hữu ích, nhưng chúng có thể cồng kềnh nếu bạn cần kiểm tra nhiều biến hoặc từng bước qua logic phức tạp. Đây là lúc trình gỡ lỗi trở nên hữu ích. Hầu hết các IDE hiện đại đều có trình gỡ lỗi tích hợp cho phép bạn:

  • Đặt điểm dừng để tạm dừng thực thi ở các dòng cụ thể.
  • Thực hiện từng dòng mã một.
  • Kiểm tra giá trị của các biến tại bất kỳ thời điểm nào.

Ví dụ: Hãy sử dụng trình gỡ lỗi Python (pdb) để gỡ lỗi hàm calculate_sum của chúng ta.

import pdb

def calculate_sum(numbers):
    total = 0
    for number in numbers:
        pdb.set_trace()  # Set a breakpoint
        total += number
    return total

# Test case
result = calculate_sum([1, 2, 3, 4])
print(result)

Khi bạn chạy mã này, quá trình thực thi sẽ tạm dừng tại điểm dừng và bạn có thể kiểm tra giá trị của totalnumber tại mỗi lần lặp lại. Điều này có thể giúp bạn nhanh chóng xác định vấn đề nằm ở đâu.

4. Viết các bài kiểm thử đơn vị

Kiểm thử đơn vị không chỉ để đảm bảo mã của bạn hoạt động chính xác; chúng cũng có thể là một công cụ gỡ lỗi mạnh mẽ. Bằng cách viết các kiểm thử đơn vị cho mã của bạn, bạn có thể cô lập các phần cụ thể của mã và kiểm tra chúng một cách độc lập. Điều này giúp dễ dàng xác định lỗi xảy ra ở đâu.

Ví dụ: Hãy viết một bài kiểm tra đơn vị cho hàm calculate_sum của chúng ta bằng cách sử dụng framework của Python unittest.

import unittest

def calculate_sum(numbers):
    total = 0
    for number in numbers:
        total += number
    return total

class TestCalculateSum(unittest.TestCase):
    def test_calculate_sum(self):
        self.assertEqual(calculate_sum([1, 2, 3, 4]), 10)
        self.assertEqual(calculate_sum([-1, 1]), 0)
        self.assertEqual(calculate_sum([]), 0)

if __name__ == '__main__':
    unittest.main()

Khi bạn chạy thử nghiệm này, bạn sẽ thấy trường hợp thử nghiệm đầu tiên không thành công, cho biết có lỗi trong hàm calculate_sum. Điều này giúp bạn thu hẹp vấn đề xuống chính hàm đó.

5. Kiểm tra những cạm bẫy phổ biến

Nhiều lỗi là do những lỗi phổ biến mà các nhà phát triển mắc phải. Một số lỗi này bao gồm:

  • Lỗi Off-by-One: Lỗi này xảy ra khi bạn lặp lại một lần quá nhiều hoặc quá ít. Luôn kiểm tra lại các điều kiện lặp của bạn.
  • Giá trị Null hoặc Không xác định: Đảm bảo bạn đang xử lý các trường hợp trong đó biến có thể là nullhoặc undefined.
  • Lỗi kiểu: Đảm bảo rằng bạn đang sử dụng đúng kiểu dữ liệu. Ví dụ, việc thêm chuỗi vào số có thể dẫn đến kết quả không mong muốn.

Ví dụ: Hãy cùng xem lại chức năng calculate_sum của chúng ta và kiểm tra những lỗi thường gặp.

def calculate_sum(numbers):
    total = 0
    for number in numbers:
        if not isinstance(number, (int, float)):
            raise ValueError("All elements in the list must be numbers")
        total += number
    return total

# Test case
result = calculate_sum([1, 2, 3, 4])
print(result)  # Expected output: 10, but getting 9

Trong ví dụ này, chúng ta đã thêm một kiểm tra để đảm bảo rằng tất cả các phần tử trong danh sách đều là số. Điều này có thể giúp phát hiện lỗi sớm hơn.

6. Nghỉ ngơi

Đôi khi, cách tốt nhất để gỡ lỗi là tránh xa vấn đề phức tạp đó trong một thời gian. Nghỉ ngơi có thể giúp bạn thanh lọc tâm trí và quay lại với góc nhìn mới mẻ. Bạn có thể chia sẻ, tâm sự chuyện nghề nghiệp với những người thân thiết, biết đâu bạn sẽ nhận lại nhiều lời khuyên hữu ích hơn.

7. Sử dụng Kiểm soát Phiên bản để có lợi cho bạn

Hệ thống kiểm soát phiên bản như Git có thể cực kỳ hữu ích khi gỡ lỗi. Nếu bạn gặp lỗi, bạn có thể sử dụng git bisect để tìm chính xác commit đã tạo ra lỗi. Điều này có thể giúp bạn tiết kiệm rất nhiều thời gian, đặc biệt là trong các cơ sở mã lớn.

Ví dụ:

git bisect start
git bisect bad  # Current commit is bad
git bisect good <commit-hash>  # Last known good commit

Sau đó, Git sẽ giúp bạn thu hẹp phạm vi commit gây ra lỗi bằng cách kiểm tra các commit khác nhau và yêu cầu bạn thử nghiệm chúng.

8. Luôn để ý Log

Logging là một giải pháp thay thế tinh vi hơn cho các câu lệnh in. Bằng cách thêm logging vào mã của bạn, bạn có thể theo dõi luồng thực thi và trạng thái của ứng dụng theo thời gian. Điều này đặc biệt hữu ích để gỡ lỗi các sự cố xảy ra trong quá trình viết mã.

Ví dụ: Hãy thêm chức năng ghi nhật ký vào hàm calculate_sum của chúng ta.

import logging

logging.basicConfig(level=logging.DEBUG)

def calculate_sum(numbers):
    total = 0
    for number in numbers:
        logging.debug(f"Adding {number} to total ({total})")
        total += number
    return total

# Test case
result = calculate_sum([1, 2, 3, 4])
print(result)  # Expected output: 10, but getting 9

Khi chạy mã này, bạn sẽ thấy các nhật ký chi tiết có thể giúp bạn theo dõi luồng thực thi và xác định lỗi ở đâu.

Cảm ơn các bạn đã theo dõi!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí