Chia đôi là cách khoanh vùng lỗi bằng việc liên tục cắt vùng nghi ngờ làm hai nửa và kiểm tra xem nửa nào chứa lỗi. Nó dùng được với dòng mã, với dữ liệu, với các phiên bản trong lịch sử thay đổi. Mỗi lần chia loại đi một nửa, nên kể cả vùng nghi ngờ có hàng nghìn thứ, bạn cũng chỉ cần khoảng mười bước.
Chào bạn, đây là Nhà Lập Trình. Hồi nhỏ chắc bạn từng chơi trò đoán số: một người nghĩ một số từ một đến một trăm, người kia đoán, được trả lời lớn hơn hay nhỏ hơn. Người chơi giỏi luôn đoán năm mươi trước. Tập này là về trò chơi đó, áp dụng vào việc tìm lỗi.
Sức mạnh của việc cắt đôi
Trong trò đoán số từ một tới một trăm, nếu đoán lần lượt một, hai, ba, bạn có thể cần tới một trăm lần. Nếu mỗi lần đoán ở chính giữa khoảng còn lại, bạn chỉ cần tối đa bảy lần. Vì mỗi lần đoán loại đi một nửa số khả năng.
Con số này tăng rất chậm. Một nghìn khả năng cần khoảng mười lần. Một triệu khả năng cần khoảng hai mươi lần. Đó là lý do chia đôi mạnh đến vậy: vùng nghi ngờ lớn đến đâu cũng thu hẹp được rất nhanh.
Trong gỡ lỗi, vùng nghi ngờ có thể là một nghìn dòng mã, mười nghìn dòng dữ liệu, hay hai trăm lần thay đổi trong ba tháng qua. Đọc từng thứ một là cả ngày. Chia đôi là vài chục phút.
Điều kiện duy nhất là bạn phải có cách kiểm tra nhanh: với nửa này, lỗi có xảy ra không. Có cách kiểm tra rồi, phần còn lại gần như là máy móc.
Chia đôi lịch sử thay đổi
Ứng dụng phổ biến và mạnh nhất là tìm lần thay đổi gây ra lỗi. Tình huống quen thuộc: tuần trước chức năng này còn chạy, hôm nay hỏng. Giữa hai thời điểm có năm mươi lần thay đổi của cả đội. Lần nào gây lỗi?
Lấy phiên bản ở giữa, lần thứ hai mươi lăm, chạy thử. Nếu có lỗi, thủ phạm nằm trong hai mươi lăm lần đầu. Nếu không, nó nằm trong hai mươi lăm lần sau. Lặp lại. Sau khoảng sáu bước, bạn có đúng một lần thay đổi.
Nhiều hệ thống quản lý phiên bản có sẵn công cụ tự động làm việc này: bạn chỉ cần đánh dấu một phiên bản tốt và một phiên bản lỗi, nó sẽ đưa bạn tới từng phiên bản ở giữa để bạn thử. Nếu bạn có sẵn một bài thử nghiệm tự động phát hiện lỗi, nó còn chạy tự động từ đầu tới cuối.
Tìm được lần thay đổi gây lỗi thì việc sửa thường rất nhanh, vì một lần thay đổi chỉ có vài chục dòng. Và bạn biết luôn ai đã viết nó để hỏi ý định ban đầu.
Chia đôi dữ liệu
Ứng dụng thứ hai là tìm dữ liệu gây lỗi. Chương trình nhập một tệp mười nghìn dòng và hỏng ở đâu đó, thông báo lỗi không cho biết dòng nào. Hãy thử nhập năm nghìn dòng đầu. Không lỗi thì thử năm nghìn dòng sau. Cứ thế cho tới khi còn một dòng.
Cách này đặc biệt hữu ích khi dữ liệu đến từ bên ngoài, như tệp bảng tính của khách hàng, dữ liệu đồng bộ từ đối tác. Thủ phạm thường là một dòng có ký tự lạ, một ô trống ở chỗ không được trống, một ngày viết sai định dạng.
Khi đã tìm ra dòng gây lỗi, đừng chỉ xoá dòng đó đi. Hãy hiểu vì sao nó gây lỗi, và sửa chương trình để xử lý được loại dữ liệu đó, hoặc ít nhất báo lỗi rõ ràng chỉ đúng dòng có vấn đề. Tháng sau khách hàng sẽ gửi một tệp khác với đúng loại dòng đó.
Có khi lỗi không do một dòng mà do hai dòng kết hợp, ví dụ hai dòng trùng mã. Khi chia đôi mà cả hai nửa đều không lỗi, nhưng gộp lại thì lỗi, đó là dấu hiệu của loại lỗi kết hợp.
Chia đôi mã nguồn
Ứng dụng thứ ba là khoanh vùng trong chính mã nguồn. Một hàm dài xử lý dữ liệu qua nhiều bước, kết quả cuối cùng sai. Hãy kiểm tra dữ liệu ở giữa hàm. Nếu ở giữa đã sai, lỗi nằm ở nửa đầu. Nếu ở giữa còn đúng, lỗi nằm ở nửa sau.
Một cách khác là tạm tắt đi một nửa. Trang web bị chậm, có hai mươi thành phần. Tắt mười thành phần, xem còn chậm không. Cách này hay dùng khi tìm thành phần nào làm vỡ giao diện, tiện ích mở rộng nào làm trình duyệt lỗi, cấu hình nào làm máy chủ không khởi động.
Cẩn thận với các phần phụ thuộc nhau. Tắt thành phần A có thể làm thành phần B không chạy được vì B cần A. Kết quả kiểm tra khi đó bị nhiễu. Hãy chia theo những ranh giới tự nhiên, nơi các phần tách được khỏi nhau.
Và luôn nhớ bật lại những gì đã tắt khi xong. Mình từng thấy một chức năng bị tắt tạm để gỡ lỗi rồi nằm im như vậy suốt vài tháng.
Khi nào chia đôi không dùng được?
Chia đôi cần một phép kiểm tra đáng tin: với mỗi nửa, bạn trả lời chắc chắn có lỗi hay không. Với lỗi lúc có lúc không, phép kiểm tra không đáng tin. Một nửa không lỗi trong lần thử có thể chỉ là may. Khi đó cần thử mỗi nửa nhiều lần, hoặc ép lỗi xảy ra đều đặn trước rồi mới chia.
Chia đôi cũng khó khi mỗi lần kiểm tra rất tốn kém, ví dụ mỗi lần phải dựng lại cả hệ thống mất một giờ. Khi đó hãy dùng hiểu biết của mình để đoán vùng khả nghi nhất trước, rồi mới chia đôi trong vùng đó.
Và chia đôi chỉ cho bạn biết lỗi ở đâu, không cho biết vì sao. Sau khi khoanh vùng, bạn vẫn cần hiểu nguyên nhân để sửa đúng.
Nhưng trong phần lớn tình huống, chia đôi là kỹ thuật đơn giản nhất mà lại nhanh nhất. Nó không cần bạn thông minh, chỉ cần bạn kiên nhẫn làm đúng từng bước. Và có lẽ đó là điều đẹp nhất ở nó.
Câu hỏi thường gặp
Tìm kiếm nhị phân và chia đôi khi gỡ lỗi có giống nhau không?
Cùng một ý tưởng: liên tục chia khoảng tìm kiếm làm đôi và chỉ giữ lại nửa chứa thứ cần tìm. Trong gỡ lỗi, thứ cần tìm là phiên bản, dòng dữ liệu hay đoạn mã gây lỗi.
Làm sao tìm lần thay đổi mã gây ra lỗi?
Đánh dấu một phiên bản còn tốt và một phiên bản đã lỗi, rồi thử phiên bản ở giữa và lặp lại. Nhiều hệ thống quản lý phiên bản có sẵn công cụ hỗ trợ việc này.
Tệp dữ liệu lớn bị lỗi khi nhập mà không biết dòng nào thì làm sao?
Chia tệp làm đôi và nhập thử từng nửa, lặp lại với nửa bị lỗi cho tới khi còn một vài dòng. Sau đó sửa chương trình để báo rõ dòng lỗi cho những lần sau.
Chia đôi có dùng được cho lỗi lúc có lúc không?
Khó hơn, vì một lần thử không lỗi chưa chắc là không có lỗi. Cần thử mỗi nửa nhiều lần hoặc tìm cách làm lỗi xảy ra đều đặn trước khi chia.
Sau khi khoanh vùng được lỗi thì làm gì?
Đọc kỹ phần đã khoanh vùng để hiểu nguyên nhân, sửa đúng gốc và thêm bài thử nghiệm để lỗi không quay lại. Chia đôi chỉ cho biết lỗi ở đâu, không cho biết vì sao.