Mọi cuộc điều tra booking mình từng chạy đều bắt đầu giống nhau: ticket đưa cho mình một chuỗi ký tự và một ý kiến rất chắc chắn. Chuỗi ký tự là một client reference, hoặc booking id, hoặc trace-id, hoặc PNR chuyến bay — thứ gì đó sống sót qua hành trình từ lời phàn nàn của khách, qua bàn support của đối tác, tới chỗ mình. Còn ý kiến thì thường là về chuyện lỗi của ai. Ý kiến đó sai đủ thường xuyên để mình học được cách coi nó là phần trang trí và bắt đầu từ chuỗi ký tự.
Dưới đây là phương pháp, viết hẳn ra thành phương pháp, vì mình nghĩ toàn bộ giá trị nằm ở thứ tự.
Điều hướng cái định danh trước khi đọc bất cứ thứ gì
Bước đầu tiên không phải là đọc log. Là hỏi: nguồn nào đổi được cái định danh này ra những cái còn lại? Một client reference resolve qua kho booking ra booking id nội bộ và reference phía supplier. Một trace-id đi thẳng vào log request. Một PNR rẽ sang phía flight rồi quay về kèm cả cái order bao quanh nó. Mỗi loại định danh có đúng một nguồn đổi được nó ra trọn bộ, và đi nhầm nguồn trước nghĩa là ngồi đọc thứ log không thể chứa chuỗi của mình, rồi kết luận sai rằng chẳng có gì xảy ra.
Vậy nên: một chuỗi vào, trọn một vòng định danh ra. Tới lúc đó việc đọc mới bắt đầu.
Timeline đi trước giả thuyết
Cầm đủ định danh rồi, mình dựng đúng một thứ và không gì khác: một timeline. Năm câu hỏi, theo thứ tự đồng hồ —
client gửi vào cái gì; mình gửi lên supplier cái gì; cái gì quay về; mình ghi gì vào kho của mình; và mình nói gì với khách.
Đó là toàn bộ bộ khung. Chưa được phép có giả thuyết nào cho tới khi cả năm cột hoặc có dữ liệu, hoặc ghi rõ "không có vết". Kỷ luật này nghe thì máy móc mà hoàn vốn ngay lượt đầu, vì phần lớn root cause sai đều sai theo cùng một kiểu: giải thích được ba trong năm điểm và lẳng lặng giả định hai điểm còn lại. Vụ bí ẩn kinh điển của booking — khách bị trừ tiền, supplier bảo không có gì — không giải được bằng ý kiến nhưng gần như cơ học khi có timeline, vì timeline chỉ ra chính xác mũi tên nào tiền đã băng qua và mũi tên nào lời xác nhận thì không.
Không cái nào ở trên chạy được nếu log không được thiết kế cho nó
Đây là phần dễ bỏ sót: phương pháp này không phải sự khôn khéo. Nó chạy được nhờ ba quyết định đặt vào từ lúc thiết kế logging, rất lâu trước bất kỳ sự cố cụ thể nào.
Một, request đi vào được chụp lại ngay dòng đầu tiên của handler, trước mọi mutation. Không phải "cái request, đại khái, sau khi parse" — mà đúng thứ client thật sự gửi, giữ nguyên trước khi defaulting, enrichment hay validation chạm vào. Thiếu cái này, mọi cuộc điều tra mở màn bằng một cuộc cãi nhau về chuyện client đã gửi gì, và cuộc cãi đó không thắng được bằng bằng chứng.
Hai, mọi cú gọi lên supplier đều được log kèm đúng request và response của nó — đúng tới mức một cú gọi hỏng có thể bắn lại y nguyên như request nó đã gửi ban đầu. Có hẳn một lệnh dựng lại request đó từ mỗi một cái trace-id. Nó biến "mình nghĩ supplier sẽ từ chối cái này" thành một phép thử chạy được, và đó là khác biệt giữa giả thuyết với sự thật.
Ba, khi mình tóm tắt một request thành bảng field, các dòng của bảng lấy từ chính validation schema của endpoint — không phải từ mấy field tình cờ lọt vào mắt. Schema là danh sách đầy đủ những gì endpoint nhận được, nên không có gì bị lặng lẽ rơi khỏi bản tóm tắt. Một danh sách field chọn tay sẽ bỏ sót đúng cái field mình tưởng không quan trọng, và đó đúng là cái field quan trọng.
Absent, empty, null
Có một thói quen xứng đáng một mục riêng vì nó định đoạt nhiều vụ. Một field trong request có ba trạng thái tách bạch, và chúng mang nghĩa khác nhau: absent (client chưa từng gửi cái key đó), empty (client gửi, cố ý để trống), và null (client gửi, chủ động xoá giá trị). Một bản tóm tắt đè phẳng ba cái này thành "không có giá trị" là phá huỷ đúng phần phân biệt mà nhiều khi chính là root cause — field absent thì rơi xuống default; null tường minh có khi lại override default đó. Khi khác biệt giữa hai hành vi ấy là một booking đặt nhầm tài khoản, thì ba trạng thái được ba ký hiệu khác nhau trong bảng field, luôn luôn.
Đoạn chuỗi mình không đọc được
Có những lúc timeline hở một khoảng mà không phải do log thiếu: một mắt xích nằm trong repo hay hệ thống mình không đọc được — middleware của đối tác, ruột gan của supplier. Cám dỗ lúc đó rất mạnh, vì timeline đã xong chín phần mười và một giả thuyết trông đẹp hơn hẳn cái còn lại.
Đầu ra trung thực trong tình huống đó không phải một root cause. Là hai giả thuyết còn sống sót, nói thẳng ra, cộng với câu query — hoặc dòng log, hoặc câu hỏi gửi team bên kia — đủ sức tách bạch chúng. Đó là một tài liệu viết ra kém đã tay hơn và nhận về hữu ích hơn nhiều, vì một root cause tự tin mà hoá ra sai sẽ khiến người kế tiếp mất một ngày tìm nhầm chỗ, còn một ngã ba kèm sẵn thủ tục quyết định chỉ tốn của họ một câu query.
Phương pháp này mua được gì
Mình chưa từng bấm giờ một cuộc điều tra có và không có cái khung này, nên không có con số tăng tốc nào để đưa ra — chỉ có một quan sát là kiểu thất bại đã đổi. Điều tra ngày trước kết thúc bằng "bọn mình nghĩ là"; giờ kết thúc bằng một timeline mà người thứ hai kiểm được không cần dựng lại từ đầu, và khi câu trả lời là "từ phía mình không thể biết", tài liệu nói thẳng như vậy và nói luôn thứ gì sẽ phân định được.
Một chuỗi ký tự vào. Một timeline ra. Giả thuyết đứng cuối cùng, và chỉ khi nó sống sót qua đủ năm cột.