סביבת הרצה (playground) של Python מבוססת דפדפן, המגיעה עם runtime בגודל 5.5MB, החלה להיכשל בשקט עבור משתמשים עם חיבורים איטיים. האשם היה שימוש שגוי ב-Network Information API ולוח בקרה (dashboard) לקיבוץ שגיאות שסימן את הבעיה בטעות. הבאג הסתתר במשך שבועות, בזבז זמן של מפתחים והותיר חלק מהמשתמשים ללא יכולת להריץ קוד.
כיצד הבעיה צפה
מעקב השגיאות של ה-playground הציג הודעה אחת בולטת: "undefined is not an object." הכותרת רמזה על טעות הקלדה (typo) פשוטה ב-JavaScript, ולכן הצוות רדף אחרי נתיב קוד שלא קיים. כשבדקו את המטא-דאטה הגולמי, הם ראו ש-89% מהתקריות הללו היו למעשה network timeouts. לוח הבקרה לקח את השגיאה הראשונה שהגיעה והשתמש בה כדי לתת שם לכל הקבוצה, ובכך הסתיר את סוג הכשל האמיתי.
שיעור 1 – כותרות בלוח בקרה יכולות להטעות
לוח בקרה שמקבץ תקריות עוזר רק אם לוגיקת הקיבוץ שלו משקפת את הסיבה האמיתית לכל אירוע. במקרה זה, קיבוץ לפי מיקום במקום לפי סיבת השגיאה הציג תמונה שגויה של באג בצד הלקוח (client-side). השורה התחתונה: לעולם אל תתקנו בעיה בהתבסס אך ורק על כותרת בלוח בקרה. שלפו דגימה של האירועים שבבסיס הבעיה וודאו מה באמת קורה לפני שאתם מקצים משאבים.
שיעור 2 – ערכי placeholder אינם מדידות
כדי להימנע מטעינת ה-runtime הכבד עבור משתמשים עם חיבורים איטיים, הקוד פנה ל-Network Information API וקרא למאפיין downlink, שמדווח על מגה-ביט לשנייה. בביקור ראשון, Chrome מחזיר לעיתים קרובות ערך זמני (placeholder) במקום מדידה אמיתית. הלוגיקה התייחסה לערך הזמני הזה כאל חיבור מהיר ודילגה על האופטימיזציה, ובכך חסמה בפועל דווקא את המשתמשים שהיא הייתה אמורה לסייע להם.
התייחסו לכל ערך ברירת מחדל או ערך סנסור (sentinel value) כ-"אין נתונים". ערך placeholder אמור להפעיל אסטרטגיית גיבוי (fallback strategy), ולא להתפרש כמדידת מהירות אמיתית.
שיעור 3 – תנאי הרשת משתנים, לכן תמונת מצב בודדת אינה אמינה
לאחר בעיית ה-downlink, הצוות עבר לבדוק את effectiveType, המסווג חיבורים כ-"4g", "3g" וכו'. בדיקת מעבדה מהירה עברה בהצלחה, אך אותה בדיקה שוב לאחר רגע נכשלה. חיבורי מובייל הם תנודתיים; משתמש יכול להופיע על חיבור 4G מהיר בשנייה אחת ולרדת ל-3G איטי בשנייה הבאה. בדיקת החיבור רק בעת טעינת הדף היא הימור.
הגישה הנכונה היא להירשם (subscribe) לאירוע ה-change באובייקט ה-Network Information ולהגיב לכל שינוי ברוחב הפס, במקום לקבל החלטה חד-פעמית.
מה הצוות שינה
- הורדה בשני שלבים – ה-runtime מתחיל כעת בקובץ bootstrap זעיר. אם החיבור מזוהה כאיטי, ה-bootstrap מוריד את שאר ה-runtime במקטעים (chunks) קטנים, מה שמפחית את הסיכוי לביטול מוחלט של ההורדה.
- ניטור חי (Live monitoring) – במקום קריאת
downlinkבודדת, הקוד מאזין כעת לאירועיchangeומתאים את אסטרטגיית ההורדה תוך כדי תנועה. - בחירת מקור יציבה – בעבר, המערכת החליפה CDNs באמצע ההורדה כאשר הופיע קצה (endpoint) מהיר יותר. בחיבור איטי, הדבר גרם להורדה להתחיל מחדש מאפס, מה שהחמיר את הבעיה. הלוגיקה החדשה נועלת את המקור למשך כל זמן ההורדה.
- כתיבות מטמון (cache) מושהותות – פעולות מטמון כבדות שרצו לפני שהאפליקציה הייתה ניתנת לשימוש נדחו כעת לשלב שאחרי שה-runtime התחיל לפעול, מה שמשחרר רוחב פס להורדה הקריטית.
ההשלכות הרחבות יותר
עבור מפתחים שבונים כלים מבוססי ווב, שונות ברשת (network variability) היא נושא מרכזי. כשל שקט בחיבור איטי מתסכל משתמשים ומעוות את הטלמטריה (telemetry), מה שמוביל צוותים לנתיב ניפוי שגיאות (debugging) שגוי. במקרה זה, פרשנות שגויה של הנתונים גרמה לשבועות של חקירה חסרת תועלת.
מה כדאי לשים לב בהמשך
שורה תחתונה: כשנתונים נראים "נקיים" מדי, הם כנראה ערך placeholder; כשכותרת בלוח בקרה מצביעה על באג בודד, חפרו עמוק יותר; וכשאתם מקבלים החלטה על סמך קריאת רשת חד-פעמית, אתם מהמרים על מטרה נעה. התאמה למציאות הזו הופכת כשלים שקטים לאירועים צפויים שניתן להתגבר עליהם.
