สคริปต์ทำความสะอาด (cleanup script) ที่รันบนแพลตฟอร์มการเผยแพร่ของเว็บไซต์ระบุผิดพลาดว่าโค้ดตัวอย่าง (code snippets) เป็นตัวแปร Liquid ที่ผิดรูปแบบ และได้ลบโค้ดเหล่านั้นออกจากทุกบทความที่มีโค้ดดังกล่าว ข้อผิดพลาดนี้ทำให้บทความเชิงเทคนิคหลายสิบโพสต์ขาดโค้ดตัวอย่างที่ผู้อ่านจำเป็นต้องใช้ นำไปสู่การย้อนกลับระบบ (rollback) อย่างเร่งด่วน และการทบทวนวิธีการทดสอบการย้ายข้อมูลเนื้อหา (content migrations) ใหม่

ข้อผิดพลาดนี้หลุดรอดมาได้อย่างไร

แพลตฟอร์มนี้ใช้ Liquid ซึ่งเป็นภาษาเทมเพลต (templating language) ที่ใช้แท็ก เช่น {{ … }} ในการทำเครื่องหมายเนื้อหาแบบไดนามิก งานบำรุงรักษาตามปกติมีจุดประสงค์เพื่อลบแท็กที่หลงเหลืออยู่ซึ่งอาจทำให้การแสดงผล (rendering) ผิดพลาด ตัววิเคราะห์ (parser) ของสคริปต์จะมองหาแท็กเปิดที่ไม่มีแท็กปิดที่สอดคล้องกัน และเมื่อพบ มันจะลบทั้งบล็อกเพื่อ "แก้ไข" ข้อผิดพลาดนั้น

ในทางปฏิบัติ ตัววิเคราะห์ไม่สามารถจดจำแท็ก {% raw %} และ {% endraw %} ที่ใช้ครอบบล็อกโค้ดได้ แท็กเหล่านี้บอกให้ Liquid ปฏิบัติต่อทุกอย่างที่อยู่ภายในเป็นข้อความธรรมดา (literal text) แต่สคริปต์ที่มีบั๊กกลับมองว่า {% raw %} เป็นตัวแปรที่ยังไม่ได้ปิด ({{% raw %}) และลบโค้ดที่อยู่รอบๆ ออกไป ข้อความแสดงข้อผิดพลาดที่ถูกบันทึกไว้คือ:

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

เนื่องจากสคริปต์ทำงานบนคลังเนื้อหาจริง (live content repository) การลบจึงเกิดขึ้นพร้อมกันเป็นจำนวนมาก โดยล้างตัวอย่างโค้ดออกจากบทความที่ได้รับผลกระทบทั้งหมดในการทำงานเพียงครั้งเดียว

สิ่งที่ได้รับผลกระทบ

บทความเชิงเทคนิคต้องพึ่งพาโค้ดตัวอย่างเพื่ออธิบายแนวคิด แสดงผลลัพธ์ และแนะนำผู้อ่านผ่านขั้นตอนต่างๆ การสูญเสียบล็อกเหล่านั้นทำให้โพสต์ส่วนใหญ่แทบไม่มีประโยชน์ บังคับให้ผู้เขียนต้องเขียนเนื้อหาใหม่ และทำลายความเชื่อมั่นในความน่าเชื่อถือของแพลตฟอร์ม สำหรับเว็บไซต์ที่สร้างชื่อเสียงจากเอกสารสำหรับนักพัฒนา (developer documentation) ที่มีคุณภาพสูง เหตุการณ์นี้จึงส่งผลกระทบต่อทั้งจำนวนผู้อ่านและความไว้วางใจจากผู้ร่วมเขียนเนื้อหา

รายละเอียดที่ผู้อ่านส่วนใหญ่อาจมองข้าม

  • การประมวลผลแบบกลุ่มโดยไม่มีการทดสอบในสภาพแวดล้อมจำลอง (sandboxing) – สคริปต์ถูกรันโดยตรงบนข้อมูลจริง (production data) แทนที่จะเป็นสำเนาสำหรับทดสอบ (staging copy)
  • การจัดการแท็กที่ไม่ครอบคลุม – มีการพิจารณาแท็ก Liquid เพียงบางส่วนเท่านั้น โดย {% raw %} ไม่ได้ถูกรวมอยู่ในรายการที่อนุญาต (whitelist)
  • ขาดการทดสอบแบบเพิ่มระดับ (incremental testing) – งานนี้ถูกนำไปใช้กับชุดข้อมูลทั้งหมดโดยไม่มีการทดสอบนำร่อง (pilot run) กับกลุ่มตัวอย่างขนาดเล็ก

สิ่งที่สามารถป้องกันเหตุการณ์นี้ได้

  • รันการย้ายข้อมูลบนสำเนา – ใช้การเปลี่ยนแปลงข้อมูลจำนวนมากกับฐานข้อมูลเวอร์ชันจำลอง (sandboxed version) ก่อนเสมอ
  • ทำ Unit-test ตัววิเคราะห์กับแท็กทุกรูปแบบ – รวมถึงกรณีขอบเขต (edge cases) เช่น บล็อก raw, แท็กคอมเมนต์ และโครงสร้างแบบซ้อนกัน (nested structures)
  • การทยอยใช้งาน (Gradual rollout) – ประมวลผลบทความจำนวนจำกัด ตรวจสอบผลลัพธ์ แล้วจึงค่อยขยายขอบเขตการทำงาน

มุมมองที่ต่างออกไป

บางคนแย้งว่าการทดสอบทุกสคริปต์บนข้อมูลสำรองทั้งหมดนั้นไม่สามารถทำได้จริงสำหรับเว็บไซต์ที่มีการเปลี่ยนแปลงอย่างรวดเร็ว และความเสี่ยงจากการสูญเสียข้อมูลนั้นน้อยกว่าความจำเป็นในการแก้ไขปัญหาอย่างเร่งด่วน แม้ว่าความเร็วจะเป็นเรื่องสำคัญ แต่ต้นทุนในการย้อนคืนการลบข้อมูลจำนวนมาก ทั้งในแง่ของเวลาของนักพัฒนาและความเสียหายต่อชื่อเสียง มักจะสูงกว่าความล่าช้าที่เกิดจากการทยอยใช้งานอย่างระมัดระวัง

สิ่งที่จะเกิดขึ้นต่อไป

ทีมงานได้กู้คืนโค้ดที่หายไปจากข้อมูลสำรองแล้ว และกำลังปรับปรุงเครื่องมือทำความสะอาดเพื่อให้รองรับโครงสร้าง Liquid ทั้งหมด พวกเขาวางแผนที่จะเผยแพร่รายงานสรุปเหตุการณ์ (post-mortem) ที่ระบุรายละเอียดขั้นตอนการทำงานของการทดสอบที่อัปเดตใหม่ และจะแบ่งปันสคริปต์ที่แก้ไขแล้วต่อสาธารณะ เพื่อให้ผู้เผยแพร่รายอื่นหลีกเลี่ยงข้อผิดพลาดแบบเดียวกัน เหตุการณ์นี้เป็นเครื่องเตือนใจว่า แม้แต่ข้อผิดพลาดในการวิเคราะห์เพียงเล็กน้อยก็สามารถลบความพยายามของผู้เขียนที่ทำมาหลายสัปดาห์ได้ ทำให้การทดสอบอย่างเข้มงวดเป็นสิ่งที่ไม่อาจละเลยได้