← לכל המאמרים

הסרנו רכיב צד־שלישי מטופס יצירת הקשר. יחד איתו נמחקה רשת הביטחון.

בשבוע שעבר הסרנו ממסר טפסים של צד שלישי מאחד מאתרי השיווק שלנו. הנימוק היה פשוט, ולדעתנו עדיין נכון: כשנקודת הקצה שבבעלותנו נכשלה, הדף היה שולח מחדש — בשקט — את השם, כתובת המייל והטקסט החופשי של המבקר אל מעבד חיצוני שמעולם לא בדקנו. מנגנון גיבוי שמופעל רק כשכבר משהו משתבש הוא הנתיב הכי פחות סביר במערכת כולה שמישהו יבחין בו. בדיוק מהסיבה הזאת הוא לא אמור להחזיק מידע אישי של אף אחד.

אז מחקנו אותו. הוספנו בדיקות שמוודאות שהוא לא יחזור. ה-CI במאגר של צד הלקוח היה ירוק, וההתנהגות מול המבקר אפילו השתפרה: במקום העברה שקטה לגורם זר, שליחה שנכשלת מציגה עכשיו הודעת שגיאה ומציעה כתובת מייל שבבעלותנו.

מה שאיש לא שם לב אליו הוא שלממסר הזה היה תפקיד שני.

קוד השגיאה שהיה בעצם העברת אחריות

ה-API שקולט את הפניות — שירות נפרד, במאגר קוד נפרד — מסתיים כך כשאף אחד משני היעדים הקבועים שלו לא קלט את הפנייה:

if not (stored or emailed):
    raise HTTPException(502, "Intake sink unavailable; retry via fallback.")

ממש מעליו, בתיעוד של המודול עצמו, ישב המשפט הבא: "הטופס הסטטי יכול ליפול בחזרה למטפל צד־שלישי — אף פנייה לא הולכת לאיבוד."

כשקוראים את שני הדברים יחד, התכנון הגיוני. ה-502 מעולם לא היה הודעת כישלון. הוא היה העברת אחריות: אות לדף שאומר "אני לא הצלחתי לקלוט את זה, קח אתה". ההבטחה שאף פנייה לא הולכת לאיבוד הייתה נכונה — אבל לא ה-API הפך אותה לנכונה. הצד שקיבל את האות בעבר הצד השני עשה זאת.

ברגע שמסירים את הצד המקבל, השולח ממשיך לפלוט בדיוק את אותו אות. שום דבר לא נופל. שום דבר לא נדלק באדום. קוד הסטטוס עדיין 502, הקוד עדיין רץ באותו ענף, וההערה עדיין מבטיחה את אותה הבטחה — רק שההבטחה כבר לא נכונה. פנייה שלא הגיעה לאף אחד משני היעדים פשוט נעצרת שם.

למה שום דבר לא נדלק באדום

זה החלק ששווה להכליל, כי זה לא סיפור על שינוי אחד רשלני. שלוש רשתות ביטחון נפרדות דיווחו על הצלחה:

  • בשני המאגרים ה-CI היה ירוק. כל אחד מהם בדק את החצי שלו כמו שצריך. החוזה חי ביניהם, ואף אחד מהם לא היה הבעלים שלו.
  • הייתה בדיקה לנתיב הכישלון. היא וידאה שנקודת הקצה מחזירה 502. הקביעה הזאת עוברת בין אם ה-502 הוא העברת אחריות, בין אם הוא תור המתנה עם ניסיון חוזר, ובין אם הוא פנייה שנעלמת לתמיד — ולכן היא לעולם לא הייתה תופסת את זה.
  • שום מדד בדשבורד לא זז. פניות עדיין מגיעות. זה נושך רק בזמן תקלה — כלומר בדיוק ברגע שבו אף אחד לא מסתכל על המספר שהפסיק לזוז.

מצאנו את זה כמה ימים אחר כך, ורק מפני שמבקר קוד שרדף אחרי משהו אחר לגמרי עצר ושאל למי בעצם ה-502 הזה מדבר.

הכלל שלקחנו מזה

כשמסירים ספק, שואלים מה הוא עשה מלבד הדבר שבגללו הסרתם אותו.

רכיבים של צד שלישי צוברים תפקידים שאף אחד לא כתב בשום מקום. ממסר הטפסים היה סיכון פרטיות, והוא היה גם תור הניסיון החוזר שלנו, גם הבופר לשעת תקלה וגם ההוכחה שפנייה לא יכולה להיעלם. בדקנו אותו כדבר אחד והסרנו אותו כדבר אחד.

אותו מבנה בדיוק חוזר בעסקים רגילים. התוסף שהורדתם כי הוא היה איטי ייצר גם את מפת האתר. סקריפט האנליטיקס שהסרתם בגלל רגולציית פרטיות היה גם מה שהתראת הזמינות שלכם ניטרה. ספק הסליקה הישן שהחלפתם גם שלח ללקוחות את החשבוניות. בכל אחד מהמקרים ההסרה נכונה, וההשפעה מדרגה שנייה בלתי נראית — כי הדבר שמפסיק לקרות מעולם לא הודיע שהוא קורה.

מה שינינו בפועל

לא החזרנו את הגיבוי — נתיב מידע אישי דרך צד שלישי הוא לא משהו שמחזירים. במקום זה עשינו שלושה דברים קטנים יותר:

  1. גרמנו לכישלון להגיד את שמו. כישלון של שני היעדים פולט עכשיו שורת שגיאה ייעודית, INTAKE_LEAD_UNSUNK, שנושאת את הפנייה עצמה. היא שומרת את פרטי הקשר במכוון: עותק שחזור שאי אפשר להשיב ממנו לאף אחד הוא לא עותק שחזור.
  2. תיקנו את המשפט. התיעוד מתאר עכשיו מה הקוד באמת מבטיח היום, ומפנה לכרטיס שמחזיק את ההחלטה מה אמור להחליף את הגיבוי. הערה שמבטיחה משהו שהקוד הפסיק לעשות גרועה יותר מהיעדר הערה — אנשים סומכים עליה.
  3. כתבנו בדיקה שמסוגלת להיכשל. לא "מחזיר 502", אלא "הפנייה עדיין ניתנת לשחזור, ומסומנת ככזו שדורשת שחזור". אחר כך שברנו את התיקון בארבע דרכים שונות כדי לוודא שהבדיקה שמה לב בכל אחת מהן. שומר שמעולם לא ראיתם נכשל הוא עדיין לא ראיה לכלום.

ורשמנו במפורש את החלק שעדיין חסר, במקום לקרוא לזה גמור: סימון בלוג בלי התראה מחוברת אליו הוא נורה אדומה בחדר ריק. מדיניות ההתראה היא שינוי נפרד, בכרטיס נפרד, ולהעמיד פנים אחרת זו בדיוק אותה טעות במקום חדש.

גרסת חמש הדקות, להסרת הספק הבאה שלכם

  • מפו את כל מי שקורא לרכיב, לא רק את מה שהרכיב עושה.
  • חפשו בקוד הבטחות בסביבה — "תמיד", "לעולם לא", "מובטח", "שום מידע לא הולך לאיבוד". כל הבטחה שהספק הפך לנכונה היא מעכשיו שלכם: או לקיים אותה, או למשוך אותה.
  • שאלו למי נתיבי השגיאה שלכם מדברים. קוד שגיאה בלי מאזין הוא לא שגיאה, הוא משיכת כתפיים.
  • בדקו אם בדיקות נתיב הכישלון שלכם היו עוברות גם בעולם שבו הכישלון לא מטופל כלל. אם כן — הן בודקות צורה, לא התנהגות.
  • בצעו את ההסרה מול שני המאגרים בו־זמנית. חוזה שנפרש על פני שני שירותים לא שייך ל-CI של אף אחד מהם.

שום דבר מזה לא דורש חבילת בדיקות גדולה יותר. הוא דורש לשאול, פעם אחת, מה הרכיב הזה החזיק בשקט.

ב-MSApps אנחנו בונים ומתחזקים פלטפורמות ווב, אפליקציות מובייל ומערכות סוכני AI, וזה בדיוק סוג הדבר שאנחנו מחפשים כשאנחנו נכנסים לקוד קיים — ההבטחות שפעם היו נכונות (אפשר לראות את סוג המערכות שאנחנו מריצים בעבודות שלנו). אם יש לכם מערכת שרשתות הביטחון בה נשחקו, דברו איתנו.

← לכל המאמרים