הגדרת אסטרטגיית ניהול גרסאות API ב-CI/CD

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

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

שאלות נפוצות

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

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

מדוע ללא ניהול גרסאות של API אינטגרציות נשברות

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

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

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

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

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

מה משתנה לאחר הטמעת חוזים מנוהלים

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

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

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

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

גישות לניהול גרסאות API: השוואת מצבים

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

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

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

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

כיצד לשלב בדיקת חוזים בצינור הבנייה

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

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

ארטיפקטים ותקנונים שנשארים בריפוזיטורי

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

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

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

מקרה בוחן: אי-התאמת גרסאות שברה את הלקוח המובייל

מה היה

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

מה שינו

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

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

מה נהיה

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

במה נבדלת גרסה מג'ורית ממינורית בחוזה?

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

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

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

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

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

ספקות טכניים לפני הטמעה

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

כמה גרסאות להחזיק במקביל וכיצד להפריד ביניהן?

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

האם אפשר להטמיע ניהול גרסאות בלי לעצור שחרורים?

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

מי אחראי לסכמות ולתאימות?

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

מה קורה עם מתודות ישנות — מוחקים אותן?

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

נדון במשימה שלכם ונחזור עם תוכנית

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

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

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