Làm sao để debug?

Làm sao để debug?

Mở đầu

Lang thang trên mạng thì đọc được một bài viết này khá hay và bổ ích, nếu như không chia sẻ lại cho ae thì thật là một tội lỗi... hehe

Trong bài viết, khi đi phỏng vấn tác giả đã được hỏi một câu như sau:

"Giả sử bạn đang vận hành một website e-commerce, và khách hàng report rằng chức năng 'Add to Cart' không chạy, thì bạn sẽ troubleshoot vấn đề này như thế nào?"

Đây là một dạng câu hỏi mà theo mình là khá hay, mặc dù hơi không rõ ràng một tí, không biết người hỏi mong muốn nhận được câu trả lời như thế nào, nhưng đây đúng là thứ công việc mình và ae đang làm hằng ngày, và có hàng tỉ cách để trả lời.

1. Hiểu vấn đề

Việc đầu tiên là phải làm rõ vấn đề, theo như bug report thì việc "không chạy" nó rất là tối nghĩa , chúng ta cần thêm thông tin, nên phải tìm cách lấy thêm thông tin từ phía user, nếu không được thì tự mình tái hiện xem có bị y vậy không?

Nhưng cuộc đời 1 ông dev chỉ biết ngồi fix bug, thường nó không êm đẹp, chuyện bắt liên lạc với user để hỏi cho ra ngô ra khoai về một con bug, trong nhiều trường hợp, nó là không tưởng :)). Chuyện tái hiện lại thì cũng hên xui, nhiều khi không một ai trong team có thể tái hiện được bug, nhưng ngoài kia, hàng trăm ngàn user vẫn gặp con bug đó mỗi ngày, bài viết này đặt bạn vào trong hoàn cảnh éo le như thế, vậy phải làm gì tiếp?

2. Phân tích vấn đề

Thiếu thông tin, và thiếu luôn bug sample để có thể điều tra trực tiếp, cách tốt nhất là vẽ ra bức tranh tổng quát về các thành phần liên quan của chức năng đang bị lỗi, rồi mình ngồi làm thầy bói. Ví dụ với chức năng "Add to Cart", ta có:

debug-overview

Theo như hình trên, chức năng giỏ hàng của chúng ta gồm có phía client:

  • Frontend: Là giao diện web dành cho người dùng thực hiện các thao tác với giỏ hàng

Phía server, gồm có 2 thành phần:

  • Backend: Là một API server có nhiệm vụ giao tiếp với frontend và ghi dữ liệu về giỏ hàng của từng user vào database, ở đây bỏ qua tính hiệu quả của việc ghi DB liên tục cho một dạng thông tin có vòng đời ngắn như giỏ hàng, để cho bài viết nó đơn giản
  • Database: Là nơi lưu thông tin giỏ hàng

Kết nối từ phía client lên server, tạm gọi là network connection, có thể bao gồm đường truyền internet, tường lửa phía server, các thiết lập có khả năng ảnh hưởng đến connection từ browser của client,...

3. Truy tìm nguyên nhân

Sau khi đã hình dung ra bức tranh tổng quát, chúng ta có thể lần lượt đưa ra giả định về khả năng lỗi của từng thành phần, cần phải nói luôn là dưới đây chỉ là một vài giả định có thể xảy ra chứ không phải là toàn bộ những lỗi có thể có:

Nếu lỗi ở network connection

Lỗi có thể xảy ra ở kết nối từ client tới server, có thể chia ra làm 2 loại lỗi chính, là những lỗi chúng ta (phía vận hành sản phẩm) kiểm soát được, và những lỗi không kiểm soát được.

Những vấn đề chúng ta kiểm soát được có thể là:

  • Thiết lập tường lửa ở server: Có thể vì một lý do gì đó mà chúng ta block một hoặc một vài user từ một vài địa chỉ IP nào đó rồi.
  • Reverse proxy, (nếu có, thực ra phân loại vô network connnection cũng chưa đúng lắm) không hoạt động: Ví dụ nginx bị lỗi config.
  • User bật ad blocker: Về lý thuyết, đây là thiết lập từ phía người dùng, thì chúng ta không kiểm soát được, nhưng không có lý do gì user bật ad blocker mà app của chúng ta lại bị ảnh hưởng, trừ khi chúng ta đã làm gì đó sai thiệt sai rồi.

Những vấn đề chúng ta không kiểm soát được:

  • Đường truyền internet của user bị cá mập cắn
  • Hệ thống mạng của user chặn kết nối đến API của chúng ta
  • ...

Nếu lỗi ở Frontend

Đứng ở góc độ frontend, những lỗi có khả năng xảy ra trong trường hợp này là:

Giả sử backend hoạt động tốt, thì phía frontend:

  • Quên xử lý sự kiện click của nút Add to Card (quên code) =))
  • Không handle đúng dữ liệu trả về của backend để render ra màn hình cho đúng

Hoặc nếu có lỗi từ backend, thì lỗi của phía frontend là:

  • Không handle trường hợp xảy ra lỗi khi gọi API
  • Không handle luôn cả request timeout,...

Hoặc cũng có trường hợp cả backend và frontend đều chạy đúng, nhưng có một lỗi không liên quan làm ảnh hưởng đến chức năng hiện tại, ví dụ như một lỗi ất ơ nào đó khi load trang, làm break cả frontend app, vì thế nên sự kiện click của nút Add to Card không chạy nữa,...

Nếu lỗi phía Backend

Từ phía backend, những lỗi có khả năng xảy ra là:

  • Quên không validate dữ liệu request lên từ phía frontend
  • Code logic để thêm một item vô cart bị sai, hoặc add lộn vô cart của một user khác
  • Không kết nối được với database, hoặc kết nối được nhưng viết raw SQL query nên khi insert vô DB thì bị lỗi cú pháp, hoặc bị SQL injection luôn mà không biết
  • Hoặc là mọi thứ hoạt động trơn tru, nhưng quên gửi data trả về cho frontend =))

Nếu lỗi phía Database

Nếu là từ phía database thì có thể là:

  • Quên bật database :))
  • Bị tràn dung lượng đĩa cứng (ai xài Wordpress với MySQL trên mấy con free host ngày xưa chắc gặp cái này hoài)

4. Đưa ra phương án khắc phục

Sau khi đã biết được nguyên nhân thì chúng ta có thể dễ dàng đưa ra cách khắc phục, nhưng quan trọng hơn hết là phải đưa ra được phương án hành động để không lặp lại vấn đề như thế này trong tương lai.

Cụ thể vấn đề lớn nhất có thể thấy từ đầu bài đó là vì sao phải ngồi vẽ rồng vẽ rắn rồi đoán mò từng bước như thế? Đó là vì chưa ai đề cập đến việc cần có một hệ thống tracking, monitoring từ cả frontend lẫn backend.

Trên thực tế, có rất nhiều công cụ giúp monitor, tracking lỗi hiệu quả, như Sentry, FullStory, hay các công cụ phân tích log như Splunk,...

Kết luận

Tóm lại, sau khi đã có bức tranh toàn cảnh, thì việc chẩn đoán rất dễ, chỉ cần một ít nghi ngờ về bản thân và đồng đội, chúng ta có thể đưa ra hàng trăm giả thuyết để có thể blame nhau, rồi cùng nhau tìm cách tái hiện, rồi fix, rồi commit, rồi test, rồi deploy, rồi xách cặp đi về nhà.

Trong bất cứ trường hợp nào, dù vấn đề xảy ra ở phía backend, database hay thậm chí là từ phía người dùng, nếu như có một dòng thông báo cụ thể trên màn hình thì cả team đã không mất thời gian vẽ rồng vẽ rắn và chỉ trỏ blame nhau như thế.

Trong công việc của ae hằng ngày sẽ có vô vàn những trường hợp khác nhau, vì thế cũng sẽ có những cách debug khác nhau. Mình chỉ copy lại bài viết này vì tác giả đã nói lên được khái quát quá trình debug rồi, mình không còn gì để viết lại hay làm gì khác, duy nhất chỉ muốn chia sẻ lại cho ae cùng ngâm hehe.

Cảm ơn các bạn đã dành thời gian đọc bài viết. Xin chào và hẹn gặp lại !

Tài liệu tham khảo