הבלוג של ינון פרק

טיפים קצרים וחדשות למתכנתים

דוקר קומפוז לפיתוח

06/05/2021

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

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

הנה קובץ docker-compose.yml קטן להמחשה:

version: "3.9"

services:
  mongo:
    image: mongo

  rabbitmq:
    image: rabbitmq

  web:
    build: ./mainapp
    command: ["/usr/bin/wait-for-it.sh", "rabbitmq:5672", "--", "bundle", "exec", "rails", "s", "-p", "3000", "-b", "0.0.0.0"]
    volumes:
      - ./mainapp:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - rabbitmq

  service:
    build: ./service
    command: ["/usr/bin/wait-for-it.sh", "mongo:27017", "--", "/usr/bin/wait-for-it.sh", "rabbitmq:5672", "--", "nodemon", "main.js"]
    volumes:
      - ./service:/myapp
    depends_on:
      - rabbitmq
      - mongo

הקובץ מתאר בסיס נתונים מונגו, תור הודעות rabbitmq, אפליקציית ריילס ראשית ו Micro Service יחיד ב node.js. הקובץ מותאם לסביבת פיתוח ובסביבת פרודקשן יהיה לנו docker-compose.yml אחר.

הקובץ מתייחס לשתי תיקיות שנמצאות איתו באותו מיקום: בתיקיה service נמצא Micro Service שכתוב ב Node.js ובתיקיה mainapp נמצאת אפליקציית ריילס. לכל אחד מהם יש Dockerfile משלו ובגלל זה השימוש ב build. אבל אותו Dockerfile מגדיר את התיקיה /myapp בתור תיקיית הבית באמצעות הפקודה:

WORKDIR /myapp

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

עם קובץ ה docker-compose.yml הזה מספיק להפעיל פעם אחת:

$ docker compose build

ואחרי זה בתחילת הפיתוח:

$ docker compose up

והמערכת עולה עם כל הסרביסים והאפליקציות ובסיסי הנתונים ומה לא.

יש לכם טריקים נוספים לפיתוח קל עם docker-compose או docker? אשמח לשמוע וללמוד, מוזמנים לשתף בתגובות.

ומה למדתם היום?

05/05/2021

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

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

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

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

אם לא תדע מה הבעיה

04/05/2021

שיחקתי היום עם המדריך הזה כדי ליצור Web API ב C#.

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

לא לדאוג - אני מתכנת. יש המון דברים שאפשר לעשות כדי לגרום לקוד לעבוד:

  1. אפשר להדביק את הודעת השגיאה בגוגל ולקוות שיעלה איזה דף מ Stack Overflow.

  2. אפשר למחוק את הכל ולהתחיל לעקוב מההתחלה אחרי ההוראות.

  3. אפשר לנסות Tutorial אחר.

  4. אפשר להיכנס לפרויקט ולהתחיל לשנות קוד באקראי או להוסיף הודעות הדפסה.

אבל האמת שיש רק דבר אחד מעניין לעשות כאן - להבין מה הבעיה. וזה קשה, כי אם אתה לא מכיר את הטכנולוגיה בכלל אז ה"להבין מה הבעיה" הולך לקחת אותך למסע כדי לבדוק איזה קוד מתחבר לאיזה Route, ומה זה בכלל Controller, ואיזה Middlewares נמצאים בשימוש ביישום ASP.NET דיפולטי, ומה זה בכלל appsettings.json, ואיך הוא מחליט על איזה פורט להקשיב, ולמה הם כותבים מילים בתוך סוגריים מרובעות מעל שמות של מחלקות או מתודות. במבט ראשון זאת לא נראית כמו הדרך הקצרה ביותר לגרום ליישום לעבוד. במבט שני קל לראות שכשיודעים לענות על כל השאלות שראינו ויודעים לחפש את הבעיה, גם הרבה יותר קל לפתור אותה עבור יישום זה ועבור היישומים הבאים שנכתוב.

כישלון ידוע מראש

03/05/2021

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

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

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

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

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

כשאני יודע יותר מהמחשב

02/05/2021

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

interface IObject {
  a: number;
  b: number;
}

const o : IObject = { a: 10, b: 20 };
const k = JSON.parse('"a"');

console.log(o[k]);

זה קוד TypeScript שלא עובר קומפילציה למרות שאין בו אף טעות.

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

ניסיון לקמפל את הקוד מקפיץ את השגיאה:

Element implicitly has an 'any' type because expression of type 'any' can't be used to index type 'IObject'.

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

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

  1. אפשר לבקש מ TypeScript שיעזוב אותי בשקט ולא ישמור על המשתנה o בכלל.

  2. אפשר להגיד ל TypeScript שאני יודע מה שאני עושה והתצואה של JSON.parse באמת הולכת להיות מפתח מתוך הממשק IObject.

הכיוון הראשון נראה כך ומתקמפל:

interface IObject {
  a: number;
  b: number;
}

const o : Record<string, any> = { a: 10, b: 20 };
const k = JSON.parse('"a"');

console.log(o[k]);

והכיוון השני נראה כך וגם מתקמפל:

interface IObject {
  a: number;
  b: number;
}

const o : IObject = { a: 10, b: 20 };
const k: keyof IObject = JSON.parse('"a"');

console.log(o[k]);

האם באמת צריך להכיר 5 פיתרונות לאותה הבעיה?

01/05/2021

שאלה זריזה (אל תספרו לי את התשובה. תענו בראש): כמה מכם, אחרי שסיימתם לכתוב פיצ'ר או לפתור באג, חוזרים אחורה עם git reset --hard ואז מנסים פיתרון נוסף? ופיתרון שלישי? ורביעי? וחמישי?

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

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

אבל...

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

ואני לא מדבר על לגלות איזו טכנולוגיה יותר מתאימה למוצר באמצעות כתיבת POC בכמה טכנולוגיות שונות. את זה אני יודע שאתם עושים. אני מדבר על בחירת המחלקות לפיתרון מסוים. אני מדבר על חלוקת העבודה בין ה Micro Services שאתם כותבים. אני מדבר על הבחירה בין כתיבת שאילתת SQL מסובכת לבין פיענוח המידע בקוד צד שרת. אני מדבר על ההתבלטות עד כמה לשנות את קוד המקור בשביל שיהיה קל לבדוק אותו לעומת כמה Mock-ים לבנות כחלק מהבדיקה. אלה שאלות מעניינות שרק אחרי שאנחנו כותבים את שתי האפשרויות אנחנו נמצאים במקום להשוות ביניהן.

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

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

האם באמת צריך להכיר 5 פיתרונות לאותה הבעיה? אם אנחנו מכוונים להיות המתכנתים הכי מקצועיים שאנחנו יכולים - התשובה בהחלט כן.

ההחמצה הראשונה

30/04/2021

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

איך זה קורה?

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

כל הרגל שאנחנו מנסים לאמץ מביא שינוי בסגנון החיים, וכל שינוי מורכב מחלקים שאנחנו יותר אוהבים וחלקים שפחות. אם תחליטו לאמץ הרגל של Life Long Learning, כלומר שילוב לימוד בתור חלק אינטגרלי מהחיים (גם כשכבר יש לכם עבודה, ולא רק בלימודים לתואר), אתם תגלו שלהשקיע כמה שעות בשבוע בלימודים לפעמים חושף אתכם לרעיונות חדשים ועוזר לכם לחשוב טוב יותר, אבל לפעמים גם מרגיש כמו בזבוז זמן כשהלכתם ללמוד משהו שלא התחברתם אליו. יותר מזה, אתם תגלו שלא בכל שבוע מתחשק לכם ללמוד ושלפעמים מטלות של לימודים באמת מרגישות כמו שיעורי בית.

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

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

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

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

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

איך ליצור Docker Image בלי Dockerfile

29/04/2021

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

  1. דוקרפייל הוא קובץ בפורמט טקסט, כך שאפשר להתייחס אליו בתור קוד מקור לכל דבר.

  2. אפשר לשמור את ה Dockerfile בגיט ולנהל עליו גירסאות.

  3. אפשר לשתף את ה Dockerfile עם חברים כדי שיוכלו בעצמם לבנות את האימג' לפי אותן הוראות.

  4. קל מאוד לתחזק אימג'ים ולשנות אותם כשיש לך ביד את ה Dockerfile שבונה את האימג'.

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

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

זה עובד ככה:

  1. יוצרים קונטיינר חדש ומריצים אותו, למשל עבור אלפין לינוקס אפשר להפעיל:
$ docker run -d --rm alpine sleep infinity
  1. נכנסים לקונטיינר ומתחילים ליצור שם קבצים או לשנות דברים במכונה (שימו לב לשנות את ה Container ID לערך שמופיע אצלכם):
$ docker exect -it 2b090ffbd1fb /bin/sh
  1. בשביל המשחק אני מפעיל בתוך המכונה את הפקודה הבאה כדי ליצור משתמש חדש:
/ # adduser ynon

אחרי שבחרתי סיסמה אני יוצא מהקונטיינר ובמכונה הראשית שלי מפעיל (שוב שימו לב להחליף את ה Container ID למה שמופיע אצלכם):

$ docker commit 2b090ffbd1fb
  1. הקומיט יצר אימג' חדש, וכשנסתכל ב docker images נוכל לראות אותו עדיין ללא שם:
<none>               <none>          3030ab2039c3   30 seconds ago   5.58MB
  1. נשאר רק לתת שם לאימג' כדי שאפשר יהיה לדחוף אותו ל docker hub (ושוב שימו לב לשנות את ה Image ID לזה שהופיע אצלכם, ואת שם האימג' למשהו שמתאים למשתמש דוקרהאב שלכם):
$ docker image tag 3030ab2039c3 ynonp/commit-demo
  1. ובסוף אפשר לדחוף את האימג' החדש לדוקרהאב כדי שכולכם תוכלו להשתמש בו:
$ docker push ynonp/commit-demo
  1. אלה מכם שירצו לשחק עם האימג' החדש שיצרתי צריכים עכשיו רק להריץ:
$ docker run -it --rm ynonp/commit-demo /bin/sh

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

ארבע שנים ניסיון

28/04/2021

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

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

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

שני טיפים על הנושא הזה:

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

  2. ואם אתם צעד אחד קודם, כלומר עובדים במקום ומוצאים את עצמכם מעתיקים ומדביקים קוד מהר רק בשביל שפיצ'רים יעבדו, בלי להבין לעומק מה אומרת כל שורה - במצב כזה שווה לעשות מאמץ אקטיבי להאט. לכתוב פחות פיצ'רים או לכתוב אותם יותר לאט, לקרוא ולהבין לעומק מה כל דבר עושה ולנסות לבנות את אותו מנגנון בכמה דרכים שונות כדי להבין מהי הדרך הטובה ביותר. ככל שתתאמצו לשלב לימודים ועבודה (במינון שמתאים לכם ולבוס), כך תגדילו את הסיכוי שאחרי 4 שנים תצליחו להמשיך הלאה ולהיכנס לעבודה הבאה.

איך למזג קבצי JSON (או כל קובץ מיוחד אחר) עם git

27/04/2021

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

דוגמה קלה היא קבצי JSON, ובמיוחד הקובץ package.json למתכנתי Front End או מתכנתי Node.JS. כשאנחנו מוסיפים מודול בענף אחד בגיט, ובענף אחר מוסיפים מודול אחר, כמעט תמיד נקבל קונפליקט. הנה דוגמה קטנה של package.json אחרי ניסיון merge, כשענף אחד התקין מודול בשם cowsay וענף אחר התקין מודול בשם mocha:

{
  "name": "driverdemo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
<<<<<<< HEAD
  "dependencies": {
    "cowsay": "^1.4.0"
=======
  "devDependencies": {
    "mocha": "^8.3.2"
>>>>>>> feature2
  }
}

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

דרייבר מיזוג, או merge driver, הוא פשוט תוכנית ש git יפעיל כשהוא צריך למזג שני קבצים. התוכנית תקבל בתור פרמטרים את שני הקבצים וגם את הקובץ בגירסת ה merge-base שלהם, כלומר הקובץ "המקורי" ממנו שניהם יצאו.

אנחנו יכולים לכתוב דרייברים ככל שנרצה ולבקש מגיט להפעיל אותם על קבצים מסוג מסוים באמצעות תוספת הגדרה בקובץ .git/config.

דרייבר מיזוג שכבר קיים ועובד טוב לקבצי JSON נקרא git-json-merge. אני ממליץ להסתכל גם על הקוד שלו כדי לראות איך נראה merge driver וכי בסך הכל הוא די פשוט. בשביל להתקין את הדרייבר נפעיל:

npm install --global git-json-merge

ועכשיו נמשיך להגדיר את git-json-merge בתור הדרייבר שימזג קבצי JSON בפרויקט שלנו. קודם כל יש ליצור בתיקיית הפרויקט קובץ בשם .gitattributes ובו התוכן הבא:

*.json merge=json

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

הקובץ השני בסיפור שלנו הוא .git/config שם נוסיף את הבלוק:

[merge "json"]
    name = custom merge driver for json files
    driver = git-json-merge %A %O %B

הפקודה git-json-merge היא התוכנית שהרגע התקנו, ושלושת האותיות אחריה מייצגות את שלושת הגירסאות של הקובץ: %A הקומיט מהענף הנוכחי, %O הקובץ המקורי מ merge-base ו %B הקובץ מהענף השני.

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

{
  "name": "driverdemo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "mocha": "^8.3.2"
  },
  "dependencies": {
    "cowsay": "^1.4.0"
  }
}

ואם יש לכם קבצים נוספים בפורמטים יצירתיים שווה להסתכל בקוד של git-json-merge ולכתוב גירסה מותאמת למיזוג הקבצים שלכם.