הגדרת כיסוי קוד ב-CI/CD: ספים, מגמות והתראות

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

היכולות שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1365
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1331
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    783
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

למה מדד כיסוי קוד משקר ב-CI/CD ושובר מהדורות

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

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

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

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

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

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

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

מה נותן ניהול כיסוי: אפקטים מדידים

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

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

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

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

גישות לשליטה בכיסוי: השוואה ומגבלות

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

SonarQube יודע לחשב את כל המדדים מריצה אחת, כך שהבחירה אינה מה למדוד, אלא איזה כלל הופך את הבדיקה לחוסמת.

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

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

הטמעה: מביקורת צינור ועד התראות כיסוי

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

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

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

מה מוסרים לידיים: כללים, קונפיגורציות, תקנונים

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

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

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

מקרה בוחן: ספים ומגמות הסירו רגרסיות מהמהדורות

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

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

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

מדדים לפני ואחרי

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

במה שונה כיסוי קוד לפי שורות ולפי ענפים?

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

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

הפער בין המדדים הוא מקור הכיסוי הכוזב. שורה עם if (a && b) נעשית "ירוקה" ברגע ש-a מתברר כשקר: החלק הימני לא יבוצע אף פעם, והדוח יראה 100%. כך מתנהגים גם החזרות מוקדמות ומטפלי חריגים — השורות סגורות, אבל תרחיש השגיאה לא נבדק.

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

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

ספקות נפוצים של מהנדסים לפני הגדרת כיסוי

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

הריצה תהיה ארוכה פי שניים — איסוף הכיסוי מעכב את הבדיקות?

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

הכיסוי מצויר בבדיקות ריקות — אז למה בכלל סף?

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

אצלנו מונוליט וקוד ישן — המאגר לא יגיע לסף הכללי.

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

בואו נדבר על המשימה: איך להגדיר ספים והתראות

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

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

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

תארו את המשימה — נחזור עם הערכה ותוכנית עבודה.