- Debugging của David J. Agans nói về nền tảng gỡ lỗi: tìm ra nguyên nhân và sửa lỗi sau khi bug được phát hiện, đồng thời đưa ra các nguyên tắc mà không chỉ lập trình viên mới vào nghề hoặc trình độ trung cấp mà cả người giàu kinh nghiệm cũng nên xem lại nhiều lần
- Cuốn sách được cấu thành từ 9 quy tắc, kết nối việc hiểu hệ thống, tái hiện lỗi, quan sát, chia để trị, kiểm soát thay đổi, lưu vết kiểm toán, kiểm tra giả định, góc nhìn từ bên ngoài và xác minh bản sửa với các ví dụ thực tế
- Dù có xuất hiện công nghệ cũ hoặc các ví dụ ngoài máy tính, cốt lõi không nằm ở công cụ cụ thể mà là tư duy thu hẹp vấn đề, nên có thể áp dụng rộng rãi cho cả gỡ lỗi phần cứng lẫn phần mềm
- Với những bug khó xử lý như lỗi xảy ra ngắt quãng, lời khuyên trong Make it Fail đặc biệt hữu ích, dù việc không đề cập trực tiếp đến thuật ngữ Heisenbug vẫn là một điểm đáng tiếc
- Khác với các sách giải thích cách dùng GDB hay cách viết test, sách tập trung vào bức tranh lớn của gỡ lỗi, nên cách dùng công cụ và kiểm thử hồi quy cần được bổ sung từ tài liệu khác
Vấn đề mà cuốn sách nhắm tới
- Debugging: The 9 Indispensable Rules for Finding Even the Most Elusive Software and Hardware Problems của David J. Agans nói về quá trình tìm ra nguyên nhân và thực sự sửa lỗi sau khi bug được phát hiện
- Thay vì xoay quanh công nghệ hay công cụ cụ thể, sách hệ thống hóa các nguyên tắc gỡ lỗi cần thiết cho nhà phát triển phần mềm và phần cứng máy tính
- Sách đặc biệt phù hợp với người mới và lập trình viên trình độ trung cấp, đồng thời cũng giúp người giàu kinh nghiệm khôi phục những nền tảng dễ bị bỏ qua trong lúc gấp gáp
- Điểm mạnh là cô đọng kỹ năng gỡ lỗi vốn thường được học qua kinh nghiệm thành các nguyên tắc và ví dụ cụ thể
9 quy tắc gỡ lỗi
-
Hãy hiểu hệ thống
- Đọc tài liệu hướng dẫn, nắm được cấu trúc tổng thể, hiểu các nguyên lý cơ bản và cách vận hành chi tiết
- Đồng thời kiểm tra xem công cụ bạn dùng cho thấy điều gì và che giấu điều gì
-
Hãy làm cho nó thất bại
- Chạy lại vấn đề và bắt đầu từ đầu để trực tiếp kích hoạt điều kiện gây lỗi
- Thay vì mô phỏng lỗi, hãy dẫn đến lỗi thật, đồng thời tìm ra các điều kiện không được kiểm soát tạo ra bug ngắt quãng
- Ghi lại mọi thứ, đừng quá tin vào thống kê, và chấp nhận rằng những điều hiếm gặp vẫn có thể thực sự xảy ra
- Đừng vứt bỏ công cụ gỡ lỗi; hãy dùng chúng để làm lộ vấn đề
-
Ngừng suy đoán và quan sát
- Trước khi bắt đầu sửa chữa phức tạp dựa trên phỏng đoán, trước hết hãy thu thập dữ liệu có thể quan sát được
- Xem xét lỗi và các chi tiết liên quan, tự tạo đo đạc nội bộ hoặc bổ sung đo đạc từ bên ngoài
- Đừng né tránh việc đào sâu, nhưng cần cẩn thận với hiệu ứng Heisenberg khi chính việc quan sát có thể làm thay đổi hành vi
- Phỏng đoán không phải kết luận, mà chỉ là công cụ để thu hẹp phạm vi tìm kiếm
-
Hãy chia để trị
- Thu hẹp phạm vi tìm kiếm bằng successive approximation, rồi xác định bug nằm về phía nào
- Dùng các mẫu kiểm thử dễ nhận biết, bắt đầu từ trạng thái lỗi để thu hẹp nguyên nhân
- Trước tiên loại bỏ những bug đã biết và nhiễu để đơn giản hóa đối tượng điều tra
-
Chỉ thay đổi một thứ mỗi lần
- Tách riêng các yếu tố cốt lõi và hiểu rõ điều gì sai trước khi sửa
- Khi kiểm thử cũng chỉ thay đổi từng thứ một và so sánh với trường hợp bình thường
- Kiểm tra xem kể từ lần cuối còn hoạt động thì đã có gì thay đổi
-
Duy trì audit trail
- Lưu lại dưới dạng audit trail những gì đã làm, theo thứ tự nào, và kết quả ra sao
- Ngay cả những chi tiết có vẻ vụn vặt cũng có thể là nguyên nhân, vì vậy hãy ghi lại các sự kiện theo cách kết nối được với nhau
- Audit trail của quá trình thiết kế cũng hữu ích cho việc kiểm thử, nên cần được ghi lại
-
Hãy kiểm tra cái phích cắm
- Nghi ngờ cả những giả định tưởng như hiển nhiên và kiểm tra lại từ đầu
- Chính công cụ dùng để tìm lỗi cũng phải được tính là đối tượng kiểm thử
-
Hãy có một góc nhìn mới
- Khi mắc kẹt một mình, hãy tìm kiếm góc nhìn mới thông qua người khác hoặc một cách giải thích khác
- Chỉ riêng việc giải thích vấn đề cho một hình nộm cũng có thể giúp sắp xếp lại suy nghĩ
- Tận dụng chuyên môn, lắng nghe người có kinh nghiệm, và ưu tiên chia sẻ triệu chứng cũng như nhờ giúp đỡ hơn là giữ sĩ diện
-
Nếu chưa sửa được thì chưa phải đã sửa xong
- Sau khi sửa, phải xác nhận xem lỗi đã thực sự được khắc phục hay chưa và kiểm chứng rằng thay đổi của bạn đã loại bỏ đúng nguyên nhân thực sự
- Vấn đề không tự biến mất, nên cần sửa cả nguyên nhân lẫn quy trình
Cách các ví dụ làm cho nguyên tắc trở nên sống động
- Nếu chỉ nhìn danh sách quy tắc thì có thể khô khan, nhưng phần giải thích chi tiết và các giai thoại ví dụ kéo các nguyên tắc vào tình huống thực tế
- Nhiều ví dụ đi sâu tới cả chi tiết kỹ thuật nên với một số độc giả có thể hơi nặng
- Dù có những ví dụ nói về công nghệ cũ, điều cốt lõi là nguyên tắc chứ không phải công nghệ cụ thể nên không phải vấn đề lớn
- Không phải mọi ví dụ đều liên quan đến máy tính; sách cũng có một ví dụ thú vị về hệ thống dây điện trong nhà
- Nếu hoàn toàn không biết gì về phần cứng và phần mềm máy tính thì sẽ khó theo kịp nhiều ví dụ
- Sau phần giải thích quy tắc là các câu chuyện nơi nhiều quy tắc được áp dụng cùng lúc, các bài tập dễ dành cho độc giả, mẹo help desk và lời kết
Những điểm nổi bật và giới hạn đáng chú ý
- Nguyên tắc “Ngừng suy đoán và quan sát” đặc biệt quan trọng
- Vì nhiều người cố sửa vấn đề bằng phỏng đoán trước khi thu thập dữ liệu để chứng minh hoặc bác bỏ giả thuyết
- “Nếu chưa sửa được thì chưa phải đã sửa xong” cũng là một nguyên tắc để lại ấn tượng mạnh
- Không chỉ cần xác nhận là đã sửa, mà còn phải hiểu nguyên nhân là gì và vì sao nó được sửa
- Bàn luận về việc “hãy kích thích lỗi xảy ra, đừng chỉ mô phỏng lỗi” không rõ ràng bằng các phần khác của sách, nhưng vẫn là điểm đáng để hiểu
- Các vấn đề ngắt quãng thường là loại khó xử lý nhất, và sách đưa ra lời khuyên trực tiếp về điều này trong Make it Fail
- Sách có nhắc tới Heisenberg nhưng không đề cập đến thuật ngữ phổ biến trong phát triển phần mềm là Heisenbug
- Heisenbug là loại bug biến mất hoặc thay đổi hành vi khi bạn cố quan sát hay cô lập nó
Khác biệt với các tài liệu khác
- Cuốn sách này khác với sách hướng dẫn công cụ hay sách về kiểm thử ở chỗ nó đặt các nguyên tắc nền tảng của gỡ lỗi vào vị trí trung tâm
- Debugging with GDB: The GNU Source-Level Debugger của Richard Stallman và cộng sự chủ yếu giải thích công nghệ hoặc lệnh của một công cụ cụ thể
- Cũng có những tài liệu chứa lời khuyên chung như Guide to Faster, Less Frustrating Debugging của Norman Matloff, nhưng không có phạm vi rộng như sách của Agans
- Các sách về kiểm thử như Software Testing Techniques của Boris Beizer tập trung vào việc viết test để phát hiện bug, còn cách sửa bug đã phát hiện thì được đề cập ít hơn tương đối
- Sau khi tìm ra bug, cần thêm bài test cho bug đó vào bộ kiểm thử hồi quy, nhưng test và kiểm thử hồi quy nằm ngoài phạm vi của cuốn sách này
Tài liệu bổ sung và những điều còn tiếc
- Website đi kèm của sách là debuggingrules.com, nơi có các liên kết thông tin liên quan và poster 9 quy tắc có thể tải về để in
- Một điểm đáng tiếc là danh sách đầy đủ các quy tắc con quan trọng để hiểu sách không được gom lại trên một trang trong sách hoặc trên website
- Sẽ còn hữu ích hơn nếu có thêm lời khuyên và ví dụ cụ thể hơn về các công cụ phổ biến như symbolic debugger, digital logic probe, hay ddd on gdb, cũng như về các kiểu vấn đề thường gặp
- Có lẽ cũng cần một cuốn sách riêng mở rộng các quy tắc này sang giải quyết vấn đề nói chung ngoài lĩnh vực máy tính, nhưng các ví dụ trong sách này lại quá kỹ thuật với độc giả không làm trong ngành máy tính
- Các nguyên tắc cơ bản thoạt nhìn có vẻ hiển nhiên, nhưng người mới vẫn cần học và người nhiều kinh nghiệm cũng cần được nhắc lại thường xuyên; cuốn sách này phù hợp cho cả việc học lẫn ôn lại đó
1 bình luận
Ý kiến trên Hacker News
Tôi cho rằng cám dỗ tai hại nhất là cố làm cho đoạn code đang hỏng chạy được bằng cách đắp thêm “bản sửa” vào nó
Code đã hỏng có quá nhiều điểm có thể thay đổi nên rất khó sửa, trong khi làm hỏng code đang chạy thì dễ hơn nhiều
Cách thay từng bóng một khi cả dây đèn Giáng sinh không sáng sẽ thất bại nếu có nhiều bóng bị hỏng
Thay vào đó, nên bắt đầu từ một ví dụ tối thiểu chạy được, rồi thêm dần từng chút cho đến khi tìm ra điểm phát sinh lỗi; trên thực tế, nhiều khi làm lại từ đầu lại tiết kiệm thời gian hơn
Trong một sự cố khó bắt ở production, một số thành viên trong nhóm đã viết lại routine có vấn đề, và phiên bản viết lại được triển khai trước trong lúc những người còn lại vẫn đang debug
Ít nhất một lần, vì không thể dành vô hạn thời gian cho một vấn đề đã “được sửa”, rốt cuộc chúng tôi không bao giờ tìm ra bug gốc
Quy tắc số 0 là đừng hoảng loạn
Deadline và khách hàng giận dữ cản trở tư duy rõ ràng, nên một quản lý giỏi và đáng tin cậy cần che chắn kỹ sư khỏi áp lực đó để họ có thể tập trung giải quyết vấn đề
Một manager tốt đã chuyển chúng tôi sang cuộc gọi khác và nói: “Cứ phớt lờ họ và tập trung đi, tôi sẽ xử lý”, và sau đó sự tôn trọng của tôi dành cho manager đó tăng lên rất nhiều
Nếu bạn không có thời gian làm cho đúng, tại sao lại nghĩ mình có thời gian làm hai lần
Ý là chặn những thứ từ phía trên rơi xuống để các kỹ sư có thể tập trung vào công việc chính
Sẽ tốt hơn nhiều nếu có thể quay về phiên bản đang chạy rồi debug mà không chịu áp lực ở mức khủng hoảng
Với mục 4 “chia để trị”,
git bisectgiúp ích rất nhiềuNếu có một commit tốt và trong hàng chục đến hàng trăm commit sau đó có một commit xấu, bạn có thể thu hẹp commit hoặc đoạn code gây vấn đề chỉ sau vài bước
Ví dụ cách dùng có ở https://nickjanetakis.com/blog/using-git-bisect-to-help-find...
Tôi đã dùng cách này để nhanh chóng khoanh vùng trong một codebase lớn xa lạ khi đang tư vấn trực tiếp; nếu không thì phạm vi có thể bị hỏng đã quá rộng
git bisect, tôi giữ kỷ luật rằng mọi commit được đưa vào nhánh “thật” đều phải tự build được, vượt qua các test đã biết tại thời điểm đó, và theo hiểu biết lúc ấy là có thể deploy đượcTôi coi điều này quan trọng hơn các nguyên tắc như giữ lại mọi phím đã gõ hay giữ cả commit “Fixes.” cuối cùng, vì những nguyên tắc đó làm cho tìm kiếm nhị phân trở nên vô dụng
Không dùng thường xuyên, nhưng chỉ cần trúng một lần với bug lớn và bí ẩn nhất là nó cung cấp manh mối tương đương nhiều ngày làm việc trong một lượt, nên hoàn toàn xứng đáng
git bisectĐó là cách so sánh hệ thống bị hỏng với hệ thống đang hoạt động và loại bỏ khác biệt một cách có hệ thống để tìm ra lỗi
Nó áp dụng được ngoài phần mềm và phần cứng; trước đây khi có hai chiếc jet ski giống nhau, việc sửa một chiếc trong khi so sánh với chiếc kia rất hữu ích
git bisect, mà là nguyên lý tìm kiếm nhị phân tổng quát hơnKhông chỉ dùng cho phạm vi commit, mà còn có thể dùng khi chia không gian hệ thống
Ví dụ nếu một workflow 10 bước bị hỏng, bạn có thể kiểm tra xem đến bước 5 có ổn không, hoặc thu hẹp xem đó có phải vấn đề phần cứng hay không
Điều này đặc biệt quan trọng khi nguyên nhân vấn đề có thể không phải là commit code trong repository mà bạn đang bisect
bisectrất tuyệt, nhưng cần phân biệt giữa “quy tắc” là triết lý và cách tư duy của cuốn sách này, với “công cụ” là lời khuyên thực dụngNgười bắt đầu từ câu “nên dùng công cụ nào?” sẽ bất lợi hơn người bắt đầu từ câu “chẳng phải trước đây cái này từng chạy sao?”
Thế giới đầy công cụ, và nếu cố nhồi hết mọi công cụ vào đầu thì bạn sẽ phát điên, nên tốt hơn là tiếp nhận triết lý trước
git bisect run. Đúng là một công cụ nhỏ đáng kinh ngạchttps://andrewrepp.com/git_bisect_run
Cần kiểm tra xem bạn có đang sửa đúng file trên đúng máy hay không
Dạo này tôi luôn tạm thời thêm một dòng gây lỗi nghiêm trọng để xác nhận rằng file hiện tại là đúng, và tùy tình huống, cả dòng hiện tại cũng đúng
Để xác nhận rằng thay đổi của mình thực sự có tác dụng
Có thêm các quy tắc bổ sung
“Tất cả là lỗi của tôi”: có thể là lỗi trình biên dịch hoặc lỗi phần cứng, nhưng rất hiếm, nên trước hết phải nghi ngờ thay đổi trong code của mình
“Khi tìm thấy bug, hãy tìm cả gia đình và bạn bè của nó”: phải nghĩ xem việc cùng loại có thể xảy ra ở đâu nữa và kiểm tra
“Hãy tối ưu cho người dùng trước, lập trình viên bảo trì thứ hai, và máy tính sau cùng”
Bản tóm tắt có ở https://blog.codinghorror.com/the-first-rule-of-programming-...
Thường trong quá trình rút gọn đoạn code gây lỗi thành một trường hợp đơn giản hơn, ta sẽ tìm ra bug trong logic của mình
Một hai lần thì thực sự còn lại kết quả đáng để báo cho nhà phát triển, và thường đó là các thư viện có không quá vài trăm người dùng nên không có nhiều test
Cuối cùng biết được đó không phải là lỗi code tưởng như bất khả thi mà là vấn đề CPU, tôi đã rất nhẹ nhõm
Dù vậy, tốt nhất vẫn là tìm kiếm nhị phân thêm vài lần trên chuỗi nguyên nhân-kết quả để chắc chắn
Lời khuyên “hãy hiểu hệ thống: đọc manual, đọc sâu mọi thứ, nắm những điều cơ bản, biết roadmap, hiểu công cụ, tra cứu chi tiết” nghe hơi kỳ lạ
Nghe như nếu code có bug thì trước tiên phải đọc toàn bộ manual 700 trang của thư viện đang dùng, đọc 7 cuốn sách liên quan, rồi một hai tháng sau mới nhìn vào bug
Tôi tự hỏi liệu có dù chỉ một lập trình viên nào thực sự làm theo lời khuyên này không
Atwood và Spolsky tạo ra Stack Overflow vào năm 2008; đó là thời người ta biết sách qua những cái tên như “Camel book” và đơn giản là có sẵn kiến thức
0. https://stackoverflow.blog/2021/12/14/podcast-400-an-oral-hi...
“Đọc sâu mọi thứ” không nhất thiết có nghĩa là “trước hết hãy đọc toàn bộ manual 700 trang của thư viện đang dùng”
Nếu bạn gặp vấn đề với
git bisect, thay vì chắp vá vài mẩu trên Stack Overflow, bạn có thể hiểu sâu hơn một chút về chủ đề này tại https://git-scm.com/docs/git-bisectNhưng sai lầm là nghĩ rằng kết quả của vài tháng làm việc chỉ là sửa qua loa một bug đơn lẻ
Mục tiêu là thực sự sửa càng nhiều bug thuộc loại đó càng tốt, và ngay từ đầu tránh viết ra chúng
Phương án thay thế là nhảy dù vào một hệ thống mình không biết, mày mò đủ thứ mà không hiểu, thấy test xanh thì gửi PR và hy vọng mình chưa làm hỏng thêm; làm việc như vậy hằng ngày gần như là ác mộng
Thêm nữa, nếu manual của thư viện bạn dùng dài 700 trang thì rất có thể bạn đang dùng sai thư viện
Ở bước 10, chẳng phải nên thêm bug vào CI test để ngăn hồi quy sao
Cần xác nhận rằng trước khi sửa thì CI fail, sau khi sửa thì pass
Đặc biệt vì có những commit hơn 5 năm tuổi, và vì là component/library nên có khá nhiều hack kỳ quặc dành cho IE
Một số test mất nhiều thời gian hoặc phức tạp để viết, cũng cần bảo trì, và ta cũng phải chấp nhận rằng bộ test không kiểm tra được mọi điều kiện biên
Điều đó có thể có nghĩa là một bug đã lên production có thể tái xuất hiện, nhưng nếu chỉ là một lỗi đơn giản thì nó cũng có thể không có khả năng tái diễn cao hơn hàng trăm lỗi tiềm ẩn khác
Rốt cuộc còn tùy tình huống, và viết test không miễn phí
Tôi đã thấy vô số lần nguyên nhân sâu xa được kích hoạt lại khiến cùng vấn đề tái phát, hoặc không ai biết rằng tôi đã sửa nên mọi người vẫn tiếp tục dùng workaround như thể bug còn tồn tại
Ngay cả sau khi đạt đến “đã sửa!”, để lại một ghi chép ngắn và phân tích nguyên nhân gốc rễ cũng sẽ giúp ích cho người khác
Sau khi test tích lũy lâu năm, CI có thể chạy nhanh đến đâu, và việc giữ chúng lại lâu dài liệu vẫn còn ý nghĩa không
Nếu muốn gieo lối tư duy này cho trẻ em, cho chính mình hoặc cho người khác, tối thiểu nên giới thiệu những tác phẩm sau
The Martian by Andy Weir https://en.wikipedia.org/wiki/The_Martian_(Weir_novel)
https://en.wikipedia.org/wiki/Zen_and_the_Art_of_Motorcycle_...
https://en.wikipedia.org/wiki/The_Three-Body_Problem_(novel)
To Engineer Is Human - The Role of Failure in Successful Design By Henry Petroski
https://pressbooks.bccampus.ca/engineeringinsociety/front-ma...
https://en.wikipedia.org/wiki/Surely_You%27re_Joking,_Mr._Fe...!
Đặc biệt thích khái niệm “gumption traps”
Nếu đã rơi vào cái bẫy của sự cứng nhắc về giá trị thì đằng nào cũng sẽ chậm lại, nên hãy chủ động giảm tốc, xem lại những nơi mình đã đi qua và kiểm tra xem những thứ mình từng nghĩ là quan trọng có thật sự quan trọng hay không
Ngay cả việc chỉ tạm nhìn chiếc máy một lúc cũng không có gì sai; đoạn nói rằng nếu quan sát nó như nhìn dây câu, sẽ có lúc một sự thật nhỏ bé rụt rè hỏi liệu ta có quan tâm không, gần như là một kim chỉ nam cho cuộc sống
Theo mình, các sự kiện cứ xảy ra nhờ những bước nhảy logic thô, và thực chất gần với fantasy phủ lên một lớp chơi chữ khoa học rất mỏng
Vài năm trước mình từng viết một bài tương tự. Lúc đó mình chưa đọc cuốn sách gốc được nhắc đến ở đây
https://explog.in/notes/debugging.html
Zine về debugging của Julia Evans cũng rất hay: https://wizardzines.com/zines/debugging-guide/
Ngay cả sau khi debug thành công, công việc vẫn chưa kết thúc
Trọng tâm của “Three Questions About Each Bug You Find” <http://www.multicians.org/thvv/threeq.html> là ba câu hỏi
Sai lầm này có ở nơi khác không, lỗi tiếp theo đang ẩn sau bug này là gì, và cần làm gì để ngăn những bug kiểu này