ทีมที่ส่ง Python runtime ขนาด 5.5 MB ไปยังเบราว์เซอร์พบว่า 69% ของข้อผิดพลาดที่บันทึกไว้ในช่วง sprint ล่าสุด ตกอยู่ภายใต้หัวข้อเดียวที่ทำให้เข้าใจผิด และ 89% ของข้อผิดพลาดเหล่านั้นแท้จริงแล้วคือ network timeout การรายงานที่ผิดพลาดนี้ทำให้เหล่านักพัฒนาหลงทางในการแก้บั๊ก และทำให้ผู้ใช้จำนวนไม่น้อยต้องเผชิญกับความล้มเหลวในการดาวน์โหลดแบบเงียบๆ (silent download failures) ซึ่งเป็นปัญหาที่เว็บแอปใดก็ตามที่รวมไฟล์ขนาดใหญ่ (large assets) สามารถพบเจอได้ในไม่ช้า
แดชบอร์ดทำให้เข้าใจผิด
ระบบติดตามข้อผิดพลาด (error-tracking system) จะจัดกลุ่มเหตุการณ์โดยอัตโนมัติตามตำแหน่งของโค้ดที่พบข้อผิดพลาดเป็นครั้งแรก หัวข้อที่ได้จึงดูเหมือนเป็นเพียงบั๊กธรรมดาใน runtime loader ทำให้ทีมต้องเสียเวลาในช่วง sprint ไปกับการไล่หาเส้นทางโค้ด (code paths) ที่ไม่เคยเกิด timeout เลย แต่เมื่อทีมสุ่มตรวจสอบข้อมูล metadata เบื้องหลัง ภาพความจริงก็ปรากฏขึ้น: ความล้มเหลวส่วนใหญ่ไม่ใช่บั๊กเลย แต่เป็นเพราะการเชื่อมต่อเครือข่ายที่หยุดชะงักจนทำให้เกิด timeout
บทเรียนที่ได้รับ: หัวข้อข้อผิดพลาดมีไว้เพื่อความสะดวก ไม่ใช่เพื่อการวินิจฉัย ควรเจาะลึกข้อมูลดิบ (raw data) เป็นระยะเพื่อตรวจสอบว่าหัวข้อที่เห็นนั้นแสดงถึงสิ่งที่เกิดขึ้นจริงหรือไม่
Browser connection API ให้ค่าที่เป็นเพียงตัวสำรอง (placeholder)
เพื่อหลีกเลี่ยงการทำให้ผู้ใช้ที่อินเทอร์เน็ตช้าต้องรอการดาวน์โหลดขนาด 5.5 MB นักพัฒนาจึงได้ตรวจสอบ Network Information API (navigator.connection) ของเบราว์เซอร์ ซึ่ง API รายงานค่า bandwidth คงที่ที่ 1.7 Mbps สำหรับผู้เข้าชมใหม่ทุกคน
เบราว์เซอร์จะส่งค่าเริ่มต้น (default value) ออกมาเมื่อไม่มีข้อมูลประวัติของผู้ใช้รายใหม่ ค่าเริ่มต้นนั้นเป็นเพียงการคาดคะเน (hint) ไม่ใช่ความเร็วที่แน่นอน เมื่อค่า placeholder เดิมปรากฏขึ้นในทุกเซสชันใหม่ นั่นเป็นสัญญาณว่า API ยังไม่ได้ถูกปรับจูน (calibrated) ให้เหมาะสมกับกลุ่มผู้ใช้ดังกล่าว
บทเรียนที่ได้รับ: ให้ถือว่าสัญญาณเครือข่ายใดๆ ที่ไม่มีการเปลี่ยนแปลงเลยเป็นเพียงค่าสำรอง (fallback) ไม่ใช่ตัวชี้วัดที่แน่นอน
การเก็บข้อมูลแบบ snapshot เพียงครั้งเดียวเชื่อถือไม่ได้
หลังจากละทิ้งการคาดคะเน bandwidth ที่ไม่น่าเชื่อถือ ทีมได้เปลี่ยนไปใช้สัญญาณอื่นที่ดูเหมือนจะใช้งานได้ในชุดทดสอบ (test suite) ของพวกเขา การทดสอบหนึ่งครั้งผ่านไปได้ด้วยดี แต่เมื่อทดสอบซ้ำสามครั้งกลับพบความล้มเหลวทุกครั้ง ความเร็วเครือข่ายนั้นมีความผันผวนอยู่ตลอดเวลา โค้ดชุดนี้ได้ทำการเก็บ snapshot เพียงครั้งเดียว ตัดสินใจแบบถาวร แล้วดำเนินการต่อแม้ว่าการเชื่อมต่อจะเปลี่ยนแปลงไปในชั่วขณะต่อมาก็ตาม
บทเรียนที่ได้รับ: อย่าตัดสินใจดำเนินการอย่างถาวรโดยอิงจากการอ่านค่าเพียงครั้งเดียวของสิ่งที่เปลี่ยนแปลงตลอดเวลา (moving target) ควรใช้การติดตามเหตุการณ์การเปลี่ยนแปลง (subscribe to change events) แทนการดึงข้อมูลเพียงครั้งเดียว (polling once)
แนวทางการแก้ไขที่ทีมนำไปใช้จริง
- ติดตามการเปลี่ยนแปลงของการเชื่อมต่อ: แทนที่จะอ่านค่า
navigator.connectionเพียงครั้งเดียว ตอนนี้โค้ดจะคอยฟังเหตุการณ์changeและตอบสนองหาก bandwidth ลดลงหรือเพิ่มขึ้นระหว่างการดาวน์โหลด - เพิ่ม "watchdog" ตรวจสอบการหยุดชะงัก: ใช้ตัวจับเวลา (timer) เพื่อยกเลิกคำขอใดๆ ที่ไม่มีความคืบหน้าหลังจากผ่านไปช่วงเวลาสั้นๆ เพื่อให้เบราว์เซอร์สามารถลองใหม่หรือใช้แผนสำรองได้
- หยุดการเปลี่ยน CDN ระหว่างการดาวน์โหลด: การเปลี่ยนแหล่งที่มาของไฟล์ขนาดใหญ่บนลิงก์ที่ช้าจะทำให้การถ่ายโอนข้อมูลต้องเริ่มใหม่จากศูนย์ ซึ่งเป็นการเสียข้อมูลที่ได้รับมาแล้วโดยเปล่าประโยชน์ ตอนนี้การดาวน์โหลดจะใช้ CDN ที่เลือกไว้ตั้งแต่แรกจนจบกระบวนการ
- ชะลอการทำงานด้าน caching ที่หนักหน่วง: งานที่ต้องเขียนข้อมูลจำนวนมากลงใน cache จะถูกเลื่อนออกไปจนกว่า runtime จะโหลดเสร็จ เพื่อรักษาเส้นทางการทำงานที่สำคัญ (critical path) ให้สั้นที่สุด
หากแดชบอร์ดของคุณแสดงภาพที่ดูเรียบร้อยจนน่าประหลาดใจ ให้ลองขุดลึกลงไป หากการวัดค่าเครือข่ายไม่มีการเคลื่อนไหวเลย ให้ถือว่าเป็นเพียงค่า placeholder และหากการเก็บ snapshot เพียงครั้งเดียวเป็นตัวตัดสินชะตากรรมของการดาวน์โหลดขนาดหลายเมกะไบต์ แสดงว่าคุณกำลังเดิมพันกับภาพลวงตา การเดิมพันเหล่านั้นจะปรากฏออกมาในรูปแบบของความล้มเหลวแบบเงียบๆ ที่กัดเซาะความเชื่อมั่นของผู้ใช้ ซึ่งเป็นสิ่งที่โค้ดที่ชาญฉลาดเพียงใดก็ไม่สามารถซ่อมแซมได้อย่างสมบูรณ์หลังจากที่มันเกิดขึ้นไปแล้ว
