ברוב המקרים, מהנדסים (או מנהלים, או ארכיטקטים) נכנסים לחברה שבה מוצר קיים, בגרסא כלשהי. מדי פעם הגרסא עולה, לפעמים יש תיקונים קלים – Minor, או שינויים בולטים יותר – Major. פעם בכמה שנים, מוציאים אפילו דור חדש לגמרי למוצר. במקרים כאלו, ההתמודדות ה"מסובכת" ביותר עם העניין היא ההחלטה האם שינוי מסויים הוא מיינור, מייג'ור או יותר מכך. בפוסט הזה, אנסה לגעת בכמה נקודות המופיעות מעט מאוד פעמים במהלך פיתוח מוצר, אולם הן בעלות השפעה מכרעת עליו: איך מחליטים בכלל על צורת הגרסא. כיוון שמדובר בנושא לא טריוויאלי, הפוסט לא יעמיק בכל האספקטים הרלוונטיים, אלא יציג את השאלות החשובות אותן יש לשאול לפני שמקבלים החלטה על מבנה הגרסאות. בהמשך, אעלה מדי פעם פוסטים שיתמקדו בנושאים ספציפיים.
גרסאות: מה חשוב לדעת?
לפני כל החלטה על צורת מספור הגרסאות, כדאי לשאול את השאלות הבאות, ולשקול את המשמעות שלהן ביחס לפתרון שבחרנו.
קהל היעד
מיהו "קהל היעד" לגרסא? מספר הגירסא בו ישתמשו המפתחים אינו בהכרח מספר הגרסא הרשמי שיוצג למשתמש. בתווך ישנם אנשי המוצר, הבדיקות, ההטמעה ואחרים. במקרים רבים מספור הגרסאות יהיה שונה לחלוטין עבור כל גוף.
תכולת הגרסא
מה בדיוק מגדירה הגרסא? השאלה נשמעת פשוטה, אבל היא מובילה ללא מעט החלטות:
גרסא מונוליטית vs. גרסא מבוזרת
אפשרות אחת היא להחליט שכל ה-Codebase יוגדר תחת גרסא אחת. אפשרות אחרת, היא לחלק את המערכת לרכיבים, כאשר כל רכיב יקבל גרסא משלו. זהו נושא ארוך ורחב שיקבל בבוא היום את הכבוד הראוי לו בפוסט משל עצמו, אולם בין היתר, הוא מעלה את השאלות –
- באיזו רזולוציה לחלק את הרכיבים השונים?
- האם כל הרכיבים מנוהלים בדרך דומה, או שיש הפרדה בין "תשתית" לבין "אפליקציות"?
- מה היחס בין רכיבים בגרסאות שונות – איזה צירוף של גרסאות רכיבים הוא "חוקי" ואיזה לא יאפשר מערכת עובדת?
- כיצד תיאכף התצורה של הגרסאות השונות לבדיקת החוקיות שלה?
- כיצד ינוהלו שלבי המוצר השונים ובאילו תצורות של גרסאות רכיבים (פיתוח, בדיקות, הטמעה, PreSale, PostSale וכן הלאה)?
גירסאות סביבת הבניה
אותה גרסת קוד עצמה תספק מוצר סופי שונה, אם תהליך הבניה לא יהיה זהה לחלוטין בכל מרכיביו. כך, למשל, גרסת קומפיילר שונה, מערכת הפעלה, קונפיגורציה של קומפיילר או לינקר, ספריות סטטיות או דינמיות, משתני סביבה ועוד – יכולים לגרום למוצר להיות שונה גם אם נוצר מאותה גרסת קוד בדיוק. יש להחליט, לפיכך, כיצד שומרים על בנייה זהה במדויק.
ארכוב תוצרים
באופן עקרוני, שימוש בגרסת קוד מקור נתונה, יחד עם הקפדה על סביבת בנייה זהה (בכל דרך שנבחר), אמורה לספק לנו תוצר זהה ב-100% (כמעט. למעט Timestamps, חישובים רנדומליים וכדומה. בכפוף לתקנון. ט.ל.ח.) למוצר המקורי. לכן, גישת קצה אחת מציעה לשמור אך ורק את הקוד, כדי להימנע מבעיות קלאסיות של שכפול מידע, כמו נפח גבוה ומיותר או, גרוע יותר, חוסר קונסיסטנטיות של הרכיבים השונים. עם זאת, לעתים נבחר לשמור גם תוצרים (כמו: תוצרי קומפילציה; תוצרי לינקג'; קבצי מניפסט שונים; קבצי קוד הנוצרים באמצעות סקריפטים, …). יכולות להיות מספר סיבות לגישה זו, למשל:
- וידוא כי יש ברשותנו גם את התוצרים, למקרה שמשהו בתהליך הייצור ישתנה בלי שנבחין בכך
- חסכון יקר בזמן, כאשר יש צורך רק לשלוף את התוצרים ולא לייצר אותם מאפס
- שימוש בתוצרים זהים במדוייק, שיש להם גם אותן חתימות זמן או md5, וכדומה
יש לחשוב היטב על המשמעויות של כל החלטה, לגבי כל שלב במחזור חיי התוכנה.
קונפיגורציה
כיצד תישמר הקונפיגורציה של המערכת? מעבר לקונפיגורציה של תהליך הבנייה (כמו flags לקומפיילר, למשל) עליה דיברנו קודם, כאן מדובר בקונפיגורציה של המערכת עצמה, למשל – Factory Settings שיאפשרו הרמת מערכת ראשונית. ניתן להתייחס לזה כאל חלק מגרסת התוכנה, ניתן להתייחס לזה כאל מידע חיצוני שיישמר במערכת אחרת, ניתן לראות מקרים בהם יש כמה קונפיגורציות בסיס לכל גרסת תוכנה… כל ההחלטות האלו משפיעות על הדרך בה ניתן להרים מערכת בגרסא נתונה.
קבצים בינאריים
איך נשמור קבצים בינאריים? האם הם יהיו ממש חלק מהגרסא, או שאולי נשמור רק path אליהם, או חתימת md5? לא מדובר כאן בהכרח על קבצי תוצר, עליהם שוחחנו קודם, אלא על "resources", כמו קבצי תמונה, וידאו או סאונד.
ליניאריות הגרסא
האם גרסא מתקדמת יותר מכילה תמיד את השינויים בגרסא שלפניה, או שייתכן כי שתי גרסאות שונות תוגדרנה במקביל, כשבכל אחת סט שינויים נפרד (למשל, לשם קסטומיזציה ללקוחות שונים)? האם ניתן להבין בבירור, בהינתן שתי גרסאות שונות, מה היחס ביניהן – האם אחת מכילה את האחרת, או אולי כל אחת מכילה סט שינויים שונה לחלוטין?
ניהול Branches
לאילו גרסאות "רצות", אנו מצפים בכל רגע נתון?
גרסאות Product
ישנם מקרים בהם תהיה תמיד גרסת מוצר רשמית אחת. למשל, במקרה של אתר אינטרנט פשוט. ישנם מקרים, בהם יהיה צורך ברור לשמור על מספר גרסאות במקביל. למשל, אם יש לנו מספר גרסאות בשטח שלא ניתן לאחד בקלות ולשמור על תאימות לאחור עבור המשתמשים. במקרים אחרים, נוכל לבחור בין אפשרויות שונות, למשל: האם להחזיק גרסא אחת בקונפיגורציות שונות (זה יכול לכלול גם ifdef#…) או מספר גרסאות שונות.
גרסאות פנימיות
גם אם נרוץ עם גרסת מוצר אחת (וודאי אם יש כמה שחיות במקביל), יהיו לנו בדרך כלל גרסאות פנימיות – למשל, גרסת dev שעליה מפתחים, גרסת prod שעברה ייצוב, גרסאות שמחזיקים מפתחים או צוותים שונים וכן הלאה. על איזו גרסא מכניסים תיקוני באגים? על איזו גרסא מכניסים פיצ'רים חדשים?
פעפוע שינויים
בכל מקרה בו מספר גרסאות חיות במקביל, יהיו שינויים רבים (אולי אפילו מרבית השינויים) שנרצה לערוך בכמה גרסאות במקביל. חשוב ליצור נוהל ברור לפעפוע שכזה, ורצוי לאכוף אותו בדרך אוטומטית ככל האפשר.
צורת הפיתוח וההכנסה ל-repository
כאן מדובר לא רק על branching נכון, אלא על כל ניהול הרוויזיות והשינויים.
חלק חשוב מהמשמעות של גרסאות פנימיות ושל תכולות שינוי נובע מצורת העבודה של המפתחים. כאן ישנה חשיבות גם לכלי ניהול התצורה והאפשרויות שהוא מציע, וגם לנהלי הפיתוח, לארכיטקטורה ואפילו למבנה הצוותים. האם ישנו branch לכל פיצ'ר או באג? ואולי branch אישי לכל מפתח? האם ישנם פיצ'רים שמערבים שני מפתחים או יותר, ודורשים branching בצורה שונה? האם כל צוות מבצע אינטגרציה ל-branch צוותי, לפני שהוא מבצע צ'ק-אין ל-branch הראשי? ישנן אפשרויות רבות ושונות, ויש לשים לב כי הדרך שנבחרה תואמת את הגישות המקובלות (או הרצויות) בחברה, ומאופשרת על ידי הכלים שנבחרו לניהול התצורה. החלטות אלו כוללות, "מלמעלה למטה", למשל, את הנהלים הבאים:
- אילו branches קיימות במערכת, לצורך הפיתוח
- איך ומתי נקבע tag על גרסא או branch נתון
- מהי תכולת "שינוי" ברוויזיות השונות (קובץ אחד? סט קבצים? סט קבצים שמכיל פיצ'ר מושלם?…)
אכיפת שימוש בגרסאות "נקיות"
האם נאפשר שימוש ב-patches במקרה הצורך, או שתמיד נגדיר גרסא נקיה וידועה? התשובה אינה תמיד טריוויאלית, כאשר יש לקחת בחשבון אילוצים כמו SLA שמחייב מענה מיידי באתר לקוח או צוות QA שמבזבז זמן רב בהמתנה לייצוב גרסא. אם נחליט כי קיימת האפשרות שמערכות – פנימיות או חיצוניות – יריצו קוד שאינו נתון בגרסא ברורה וידועה, יש צורך לוודא כי ניהול ה-patches נעשה בדרך שמאפשרת חזרה מדויקת למערכת בכל נקודת זמן בהמשך.
שמות הגרסאות
זה אולי נשמע ברור לחלוטין, אבל במו עיני ראיתי לא מעט חברות שנופלות בדבר הדי-בסיסי הזה. קראנו לגרסא "אלפא" – האם זה אומר שלקוחות יקבלו אותה או שהיא פנימית לחלוטין? ואם זו "ביתא"? או "Pre-Beta", או "Final Alpha Clean"? לפעמים שם גרסא מוחלט ללא מחשבה עמוקה, או מסיבות פוליטיות ("התחייבנו ללקוח לספק גרסת ביתא עד אפריל", "הפרודאקט דורש מהפיתוח שבדיקות על גרסת 3.0 יתחילו כבר בשבוע הבא". כאלה…) והוא יוצר בהמשך בלבול רב. או, יותר מוצלח מזה – מקרים בהם תוך כדי תנועה משנים שם של גרסא מסיבות כאלו. יצא לי להיות בחברה שבה אחת לכמה שבועות נשלח מייל לכל אנשי הפיתוח, האומר דברים בסגנון "הגרסא שנקראה עד כה 'אלפא' תיקרא מעתה 'ביתא פנימית'. במקום גרסא 'ביתא' נשתמש מעכשיו בשם 'ביתא חיצונית'. כל מפתח העובד על פיצ'ר שמוגדר להכנסה לגרסת 'אלפא' או 'ביתא' יעדכן את הפיצ'ר או הבאג בהתאמה. תודה!"
סיכום
כפי שאמרתי בתחילת הפוסט, מדובר בנושא לא פשוט, שכל חלק בו ראוי להתייחסות בהרחבה. ניסיתי לרכז כאן את הנקודות העיקריות הנוגעות להחלטה על מבנה, מספור ושיום הגרסאות השונות, ובעתיד אכסה בצורה מפורטת יותר נושאים הדורשים העמקה.
מעניין – אני מקוה שמתישהו תספק גם תשובות לשאלות שהעלית 🙂
כדוגמה לשיקולים תוכל להביא את גרסאות Windows החל מוויסטה (6.0), 7 (6.1, למה מה חשבתם?), 8 (6.2 כמובן) ו-8.1 (6.3, מתמטיקה אלמנטרית). הסיבה כאן היתה בדיקת גרסת מערכת ההפעלה שמבצעות תוכנות התקנה (בעיקר דרייברים) – מיקרוסופט החליטו שהם שברו מספיק דברים עם ויסטה, ורצו שתוכנות שבודקות MajorWindowsVersion == 6 יסכימו להתקין את עצמן על המערכות הבאות.
שעורי בית: מה הגרסה של Windows 10?
מעניין. לא ידעתי 🙂
אכן עניין חשוב – ומתקשר גם לפוסט הקודם שבו דנתי בתאימות לאחור.
[…] הפוסט פורסם לראשונה בבלוג ״ארכיטקט בכפכפים״. […]
[…] הפוסט פורסם לראשונה בבלוג ״ארכיטקט בכפכפים״. […]
Nice but not applicable.