Một tập lệnh dọn dẹp chạy trên nền tảng xuất bản của trang web đã nhầm lẫn các đoạn mã (code snippets) là các biến Liquid bị lỗi định dạng và xóa sạch chúng khỏi mọi bài viết có chứa các đoạn mã đó. Sự cố này đã khiến hàng chục bài viết kỹ thuật bị mất đi các đoạn mã ví dụ mà độc giả thường dựa vào, dẫn đến việc phải khôi phục dữ liệu khẩn cấp và xem xét lại cách thức kiểm thử các quá trình di chuyển nội dung (content migrations).

Cách thức lỗi này lọt qua hệ thống

Nền tảng này sử dụng Liquid, một ngôn ngữ tạo mẫu (templating language) giúp đánh dấu nội dung động bằng các thẻ như {{ … }}. Một tác vụ bảo trì định kỳ đáng lẽ phải loại bỏ các thẻ dư thừa có thể gây lỗi hiển thị. Trình phân tích cú pháp (parser) của tập lệnh đã tìm kiếm các thẻ mở thiếu thẻ đóng tương ứng, và khi tìm thấy, nó đã xóa toàn bộ khối đó để "sửa" lỗi.

Trên thực tế, trình phân tích cú pháp đã không nhận diện được các thẻ {% raw %}{% endraw %} dùng để bao quanh các khối mã. Các thẻ này yêu cầu Liquid xử lý mọi thứ bên trong dưới dạng văn bản thuần túy (literal text), nhưng tập lệnh lỗi lại coi thẻ mở {% raw %} là một biến chưa được đóng ({{% raw %}) và xóa luôn đoạn mã xung quanh. Thông báo lỗi được ghi lại là:

Liquid syntax error: Variable '{{% raw %}' was not properly terminated.

Vì tập lệnh hoạt động trực tiếp trên kho lưu trữ nội dung đang chạy (live content repository), việc xóa bỏ đã diễn ra hàng loạt, quét sạch các ví dụ mã từ tất cả các bài viết bị ảnh hưởng chỉ trong một lần chạy.

Những gì đang bị đe dọa

Các bài viết kỹ thuật phụ thuộc vào các đoạn mã để minh họa các khái niệm, tái hiện kết quả và hướng dẫn độc giả thực hiện các quy trình từng bước. Việc mất đi các khối mã này khiến các bài viết phần lớn trở nên vô dụng, buộc các tác giả phải viết lại nội dung và làm xói mòn niềm tin vào độ tin cậy của nền tảng. Đối với một trang web xây dựng uy tín dựa trên các tài liệu dành cho nhà phát triển chất lượng cao, sự cố này đe dọa cả lượng độc giả lẫn thiện chí của những người đóng góp nội dung.

Những chi tiết mà hầu hết độc giả bỏ lỡ

  • Xử lý hàng loạt mà không có môi trường thử nghiệm (sandboxing) – Tập lệnh đã được thực thi trực tiếp trên dữ liệu thực tế (production data) thay vì trên một bản sao thử nghiệm (staging copy).
  • Xử lý thẻ không đầy đủ – Chỉ một nhóm nhỏ các thẻ Liquid được tính đến; thẻ {% raw %} đã bị bỏ sót khỏi danh sách trắng (whitelist).
  • Thiếu kiểm thử tăng dần – Tác vụ đã được triển khai trên toàn bộ tập dữ liệu mà không có đợt chạy thử nghiệm (pilot run) trên một mẫu nhỏ.

Những gì có thể đã ngăn chặn được sự cố

  • Chạy di chuyển dữ liệu trên một bản sao – Áp dụng bất kỳ quá trình chuyển đổi hàng loạt nào lên một phiên bản cơ sở dữ liệu trong môi trường sandbox trước.
  • Kiểm thử đơn vị (unit-test) trình phân tích cú pháp với tất cả các biến thể của thẻ – Bao gồm cả các trường hợp biên (edge cases) như các khối raw, thẻ chú thích và các cấu trúc lồng nhau.
  • Triển khai dần dần – Xử lý một số lượng bài viết hạn chế, xác minh kết quả, sau đó mới mở rộng quy mô.

Quan điểm ngược lại

Một số người lập luận rằng việc kiểm thử mọi tập lệnh trên một bản sao lưu đầy đủ là không thực tế đối với các trang web có tốc độ thay đổi nhanh, và rủi ro mất dữ liệu có thể được chấp nhận để đổi lấy nhu cầu sửa lỗi nhanh chóng. Mặc dù tốc độ là quan trọng, nhưng chi phí để khắc phục một đợt xóa hàng loạt – cả về thời gian của nhà phát triển lẫn thiệt hại về uy tín – thường lớn hơn sự chậm trễ do việc triển khai thận trọng gây ra.

Những gì cần theo dõi tiếp theo

Nhóm phát triển đã khôi phục các đoạn mã bị mất từ các bản sao lưu và đang sửa đổi công cụ dọn dẹp để nhận diện tất cả các cấu trúc Liquid. Họ có kế hoạch công bố một báo cáo phân tích sau sự cố (post-mortem) chi tiết về quy trình kiểm thử đã được cập nhật, đồng thời chia sẻ công khai tập lệnh đã sửa đổi để các nhà xuất bản khác có thể tránh được sai lầm tương tự. Sự cố này là một lời nhắc nhở: ngay cả một lỗi phân tích cú pháp nhỏ nhất cũng có thể xóa sạch công sức viết lách trong nhiều tuần của tác giả, khiến việc kiểm thử nghiêm ngặt trở thành điều bắt buộc.