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

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

היום למדתי: פרופילים ב docker compose

12/04/2023

מה קורה אם אתם צריכים להפעיל גם משימה חד פעמית בפרויקט שמשתמש ב docker compose? טוב דרך אחת היא להתחבר לקונטיינר שכבר מריץ את היישום ולהפעיל את המשימה מתוכו. לדוגמה נניח שיש לי את התוכן הבא ב docker-compose.yml:

version: "3.9"
services:
  backend:
    image: backend

  db:
    image: mysql

ויש לי סקריפט בשם myapp migrate שאני יכול להפעיל רק מתוך האימג' של backend, אז אני יכול להעלות את הסרביסים וכשצריך להפעיל את הסקריפט:

$ docker compose up -d
$ docker compose run backend bash -c "myapp migrate"

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

הייתי מעדיף לכתוב סרביס עבור הסקריפט ב docker-compose.yml שיכלול גם את הפקודה ויאפשר להריץ את הסקריפט עם הפרמטרים שאני צריך והאימג' הרלוונטי:

version: "3.9"
services:
  backend:
    image: backend

  db:
    image: mysql

  migrate:
    image: backend
    command: myapp migrate

אבל עכשיו יש לי בעיה חדשה - הסקריפט ירוץ כל פעם שאני כותב docker compose up, ובשביל למנוע ממנו לרוץ אני צריך להעביר במיוחד את רשימת כל הסרביסים האחרים ל docker compose up.

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

version: "3.9"
services:
  backend:
    image: backend

  db:
    image: mysql

  migrate:
    image: backend
    command: myapp migrate
    profiles: ["tools"]

עכשיו אני יכול להפעיל docker compose up כדי להפעיל את המערכת הרגילה, או docker compose run migrate כדי להפעיל את סקריפט המיגרציה.

ניהול מוטיבציה

11/04/2023

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

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

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

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

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

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

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

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

יבוא מעגלי ו Redux Tolkit

10/04/2023

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

המשך קריאה

טיפ פייתון: שימו לב ל b

09/04/2023

המודול base64 בפייתון מציע דרך פשוטה לפענח מחרוזת מקודדת ל base64. לדוגמה:

>>> import base64
>>> base64.b64decode("bmluamE=")
b'ninja'
>>> base64.b64decode("bW9ua2V5")
b'monkey'
>>> base64.b64decode("bG92ZQ==")
b'love'
>>>

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

import base64
import sys

enc = sys.argv[1]

users = ["monkey", "love", "ninja"]

if base64.b64decode(enc) in users:
    print("welcome")

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

אופציה 1:

import base64
import sys

enc = sys.argv[1]

users = [b"monkey", b"love", b"ninja"]

if base64.b64decode(enc) in users:
    print("welcome")

אופציה 2:

import base64
import sys

enc = sys.argv[1]

users = ["monkey", "love", "ninja"]

if base64.b64decode(enc).decode("utf-8") in users:
    print("welcome")

למה לא נשלח מייל הבוקר (או: מה בא אחרי I)

08/04/2023

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

hebcal | head -10
1/3/2023 Asara B'Tevet
1/23/2023 Rosh Chodesh Sh'vat
2/6/2023 Tu B'Shvat
2/18/2023 Shabbat Shekalim
2/21/2023 Rosh Chodesh Adar
2/22/2023 Rosh Chodesh Adar
3/4/2023 Shabbat Zachor
3/6/2023 Ta'anit Esther
3/7/2023 Purim
3/8/2023 Shushan Purim

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

evenings_ok=(
"Pesach I"
"Pesach VII"
"Shavuot I"
"Tish'a B'Av"
"Rosh Hashana II"
"Yom Kippur"
"Sukkot I"
"Shmini Atzeret"
)

for send_in_evening in "${evenings_ok[@]}"
do
        # echo "^[0-9/]+ ${send_in_evening}"
        if hebcal $(date +"%m %d %Y") | egrep -v "CH''M" | egrep "^[0-9/]+ ${send_in_evening}" >& /dev/null
        then
                if (( $(date +%"_H") < 20 ))
                then
                        exit 1
                fi
        fi
done

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

hebcal | grep 'Pesach I'
4/6/2023 Pesach I
4/7/2023 Pesach II
4/8/2023 Pesach III (CH''M)
4/9/2023 Pesach IV (CH''M)

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

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

$ hebcal | grep 'Pesach I$'
4/6/2023 Pesach I

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

evenings_ok=(
"Pesach I$"
"Pesach VII"
"Shavuot I$"
"Tish'a B'Av"
"Rosh Hashana II"
"Yom Kippur"
"Sukkot I"
"Shmini Atzeret"
)

no_emails=(
"Rosh Hashana"
)

no_emails_erev=(
"Erev Pesach"
"Erev Shavuot"
"Erev Rosh Hashana"
"Erev Sukkot"
"Erev Yom Kippur"
)


for send_in_evening in "${evenings_ok[@]}"
do
        # echo "^[0-9/]+ ${send_in_evening}"
        if hebcal $(date +"%m %d %Y") | egrep -v "CH''M" | egrep "^[0-9/]+ ${send_in_evening}" >& /dev/null
        then
                if (( $(date +%"_H") < 20 ))
                then
                        exit 1
                fi
        fi
done

for holiday in "${no_emails[@]}"
do
        if hebcal $(date +"%m %d %Y") | egrep "^[0-9/]+ ${holiday}" >& /dev/null
        then
                exit 1
        fi
done



for erev_holiday in "${no_emails_erev[@]}"
do
        if hebcal $(date +"%m %d %Y") | egrep "^[0-9/]+ ${erev_holiday}" >& /dev/null && [$(date +"_H") > 19]
        then
                exit 1
        fi
done

טיפ פייתון: פיתוח עם docker compose

07/04/2023

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

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

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

בשביל לפתח על המחשב שלנו קוד פייתון עם docker-compose נצטרך להתמודד עם האתגרים הבאים:

  1. נרצה שהקוד בתוך הקונטיינר יהיה זהה לקוד בספריית הפיתוח.

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

  3. נרצה לאפשר לקוד לתקשר עם בסיס נתונים שגם יעלה מתוך ה compose.

  4. נעדיף לא לבנות אימג' בשביל הפיתוח כדי לחסוך פעולות build.

המשך קריאה

פתוחים לרעיונות חדשים

06/04/2023

מעבר לצורך להישאר רלוונטים, יש עוד שני יתרונות בגללם אנחנו חייבים להישאר פתוחים לרעיונות חדשים-

  1. חלק מהרעיונות יכולים ללמד אותנו דרכי עבודה חדשות עם אותם כלים שאנחנו כבר מכירים.

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

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

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

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

משחקים עם React Server Components

05/04/2023

פיצ'ר Server Components של ריאקט הוא חלק מריאקט 18 ומאפשר סוג של Server Side Rendering לקומפוננטות ספציפיות. בגירסה 13 של Next.JS התווספה תמיכה ב Server Components מה שהפך את העבודה איתם להרבה יותר פשוטה. בואו נראה איך זה עובד ומה היתרונות שלהם על פני פיתוח ריאקט רגיל או על פני Server Side Rendering.

המשך קריאה

הסיבות בגללן בחרנו Micro Services

04/04/2023

יש לא מעט סיבות שיכולות לשכנע אנשים לבחור בארכיטקטורת Micro Services, ביניהן:

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

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

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

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

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

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

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

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

גיט add הכל הוא הרגל רע

03/04/2023

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

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

במהלך הפיתוח ובמיוחד בתוך רצף עבודה מאוד מפתה להשתמש בפקודות:

$ git add .
$ git commit

או בגירסה המתונה יותר git commit -a. הבעיה בשתי הגירסאות נובעת משילוב שני גורמים:

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

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

מה עושים במקום? האפשרות המועדפת עליי היא להפעיל git status ו git diff לפני שמתחילים להכניס דברים לקומיט, ואז להכניס את השינויים לקומיט בצורה מבוקרת - או באמצעות הוספת קבצים מלאים או אפילו באמצעות git add -i שמאפשר לבחור איזה שורות בקובץ צריכות להיכנס לקומיט.

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