שלוש שאלות (ותשובות) על תקיעות
אף אחד לא אוהב להרגיש תקוע אבל ההרגשה הזאת היא חלק מהחיים והיא אפילו יכולה לעזור ולדחוף אותנו החוצה ממצבים בעייתיים. בואו נבין איך היא עובדת בעזרת שלוש שאלות ותשובות על תקיעות.
טיפים קצרים וחדשות למתכנתים
אף אחד לא אוהב להרגיש תקוע אבל ההרגשה הזאת היא חלק מהחיים והיא אפילו יכולה לעזור ולדחוף אותנו החוצה ממצבים בעייתיים. בואו נבין איך היא עובדת בעזרת שלוש שאלות ותשובות על תקיעות.
"אני מתכנת ווב. התחלתי לכתוב אתרים ב Perl/CGI עוד ב 1998, מעתיק קודים מהאינטרנט כדי להציג מונה ביקורים וכאלה. אחר כך למדתי JavaScript כדי להראות באנרים מהבהבים וזזים וכמובן CSS. את PHP לא אהבתי למרות שעבדתי איתו יחסית הרבה. החל מ 2010 אני מתכנת Front End כותב אך ורק קוד צד לקוח ב JavaScript, TypeScript וכמובן בכל ה Frameworks שהולכים היום."
"אני מתכנתת צד-שרת. התחלתי לכתוב שרתים ב EJB1 עוד ב 1998 ומאז עד 2010 המשכתי לכתוב XML-ים, לתרגם אותם ולשלוח אותם בין מערכות. כשהעולם כולו עבר ל JSON שמחתי להתחיל לכתוב חלקים קטנים באפליקציה ב Node.JS ולהחליף את ה Java ב Scala. היום אני מפתחת בעיקר Serverless על AWS כותבת פונקציות Lambda שעובדות הרבה יותר טוב מה EJB המסורבל מפעם."
"התחלתי לכתוב אפליקציות מובייל ב 2005 עם J2ME. הממשק היה קשוח ומעט מאוד אנשים חיפשו אפליקציות מובייל באותה תקופה, אבל אני שמח שהיה לי שכל לעבור לפתח לאייפון איך שיצא ומשם להמשיך לכתוב אפליקציות אנדרואיד (זה היה קל עם הניסיון שכבר היה לי ב Java). כשהגיע שיגעון ה Cross Platform הייתי צריך ללמוד JavaScript וכלים מוזרים כמו פונגאפ, אבל מהר מאוד זה עבר והמשכנו ל React Native ו Flutter. היום אני מפתח Flutter ומשלב קוד Cross Platform עם קוד Native בכל פלטפורמה כדי להגיע לחווית משתמש אופטימלית"
כל אחד משלושת הסיפורים מייצג אנשים מנוסים שמסתדרים טוב בשוק העבודה היום, בזכות אותו ניסיון. הסיפור שמוכרים לנו שבהייטק הקריירה נגמרת בגיל מסוים או שהעולם שייך לצעירים הוא פייק. כל טכנולוגיה חדשה מבוססת על עולם מושגים קיים ועל טכנולוגיות קודמות.
ניסיון אפקטיבי, קצת כמו ריבית דריבית, הוא הרעיון שההתמדה משתלמת, שככל שאנחנו יותר זמן בתחום מסוים כך אנחנו מעלים את הערך שלנו.
את רוב נושאי הלימוד אין בעיה לשבור לחלקים קטנים כדי ללמוד אותם בשלבים ובצורה מסודרת לפי קצב הלימוד שמתאים לנו. אבל, להבין איך החלקים השונים מתחברים חזרה למיומנות אחת זה כבר סיפור הרבה יותר מסובך.
זאת הסיבה שתלמידי בית ספר יכולים ללמוד 4 שנים ערבית, ואחרי זה לא מסוגלים לנהל שיחה או אפילו לקרוא ספר. וזאת הסיבה שאנחנו שוכחים מיד אחרי המבחן את כל מה שלמדנו.
בלי החיבורים אי אפשר לייצר שימוש משמעותי במה שלמדנו, אי אפשר לשלב אותו בחיי היום יום ולכן הרבה יותר קשה להמשיך ולזכור.
בתכנות בדרך כלל לומדים את החיבורים מכתיבת פרויקט. ככל שהפרויקט יותר גדול ומשלב יותר מנגנונים, כך אנחנו "נאלצים" לגלות איך המנגנונים השונים שלמדנו מתחברים ועובדים יחד ובאיזה מנגנון שווה להשתמש בכל סיטואציה. הבעיה עם כתיבת פרויקט, קצת כמו הבעיה עם לקרוא ספר בשפה זרה, היא שבתחילת הדרך זאת משימה ממש ממש קשה.
וכן בדוגמת הספר, אפשר להסתכל במילון בכל מילה ובכל משפט, אבל בדיקה כזאת שוברת את הרצף ומזיזה את המבט מהחיבורים בחזרה לדברים עצמם שמתחברים, כלומר המילים או המשפטים, ושוב אנחנו חוזרים ללמוד אוצר מילים או דקדוק במקום ללמוד לראות את התמונה הגדולה.
ובאותו אופן בדוגמת הפרויקט אם כל פעם שאנחנו צריכים להתקדם נעצור ללמוד איך שני מנגנונים מסוימים צריכים לעבוד יחד, אנחנו לעולם לא נוכל להתקדם בפרויקט ושוב נעצור ללמוד עוד נושא ועוד נושא.
הדרך לצאת מהמעגל מורכבת משני חלקים: קודם כל יש לבחור פרויקט ברמה שלכם. הכוונה לפרויקט שאתם יכולים לכתוב בלי לעצור כל רגע לבדוק איך דברים מסוימים מתחברים, ובטח לא ללמוד דברים חדשים במהלך כתיבת הפרויקט. רק כשאנחנו מכירים כבר את כל החלקים אפשר להתאמן על לחבר אותם יחד לפרויקט גדול יותר.
והחלק השני כשאתם כבר בתוך פרויקט שהוא ברמה שלכם הוא להיות בסדר עם דילוגים, טעויות וחיבורים לא נכונים. המטרה היא לא לחבר את הכל בצורה האידאלית, אלא לחבר את הדברים בצורה עובדת. בשלב שני אפשר ורצוי להסתכל אחורה, למצוא בעיות בחיבורים ולשפר.
גם כשאתם מרגישים שאתם כבר יודעים את כל החלקים של נושא מסוים, ואולי דווקא כשאתם מרגישים את זה, זה הזמן להתחיל להתאמן על החיבורים.
אנחנו יודעים איך להרים סרביס בדוקר עבור בסיס נתונים או שרת API, אבל איך מרימים סרביס שיריץ משימה מתוזמנת, למשל יריץ סקריפט מסוים פעם ביום? והאם זה צריך להיות סרביס נפרד?
נתחיל בתשובה השניה: אפשר בהחלט להריץ משימה מתוזמנת מתוך שרת שרץ בתוך קונטיינר, למשל באמצעות יצירת Cron Job על אותו השרת כחלק מהאימג'. ובכל זאת אני לא אוהב את הפיתרון הזה כי אני לא רוצה לחשוב על משימות מתוזמנות ומה קורה להן כשאני מוסיף קונטיינרים. הרבה יותר נוח להפריד את המשימות המתוזמנות לסרביסים משלהן ואת שרתי ה API לסרביסים אחרים.
עכשיו לתשובה הראשונה ומסתבר שגם היא לא מסובכת. בשביל להרים סרביס שפעם ביום יבצע איזושהי פעולה אני מגדיר ב Dockerfile שיריץ את הפקודה cron ובנוסף יוצר Cron Job כחלק מהאימג' שיריץ את המשימה המתוזמנת. כאן יש דוגמה שמצאתי למאגר עם כל הקבצים: https://github.com/cheyer/docker-cron
אבל היא לא עבדה כשניסיתי ולכן הייתי צריך לעדכן חלק מהקוד. אלה הקבצים שכן עבדו לי:
הקבצים המרכזיים משם הם הקובץ crontab
שמגדיר את זמני הביצוע:
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * command to be executed
* * * * * /script.sh
# An empty line is required at the end of this file for a valid cron file.
הקובץ script.sh
שמכיל את הסקריפט שצריך להריץ:
echo "$(date): executed script"
וכמובן הכי חשוב הוא ה Dockerfile:
FROM alpine:3.6
COPY crontab /etc/crontabs/root
COPY script.sh /
RUN chmod +x /script.sh
CMD ["crond", "-f", "-d", "8"]
בניה של האימג' והרצה תתן לכם את פלט המשימה המתוזמנת ל STDOUT של הקונטיינר כך שאפשר יהיה לראות את הפלט עם docker logs
.
לאחרונה גיליתי את ספריית msw שהפכה תוך רגע לספריית ה Mock האהובה עליי, ואני בכלל לא אוהב להשתמש ב Mock. אני רוצה לשתף כאן דוגמה קצרה לספריה ודרך זה לראות מה כל כך מלהיב בה.
רציתי לכתוב בדיקות איך שהיה לי את הרעיון הראשון, עוד לפני שורת הקוד הראשונה, כדי שאוכל "לשחק" עם ה API שתכננתי ולראות אם הוא הגיוני. אבל לא היתה לי סבלנות לחכות ומיהרתי פשוט לכתוב את הקוד.
רציתי לכתוב בדיקות אחרי שכתבתי את המימוש הראשון כדי לראות אם זה עובד, אבל היה לי יותר קל להיכנס ולבדוק בדפדפן.
רציתי לכתוב בדיקות אחרי התיקון הראשון, ואחרי התיקון השני וגם העשירי, אבל כל פעם הסקרנות ניצחה והבדיקה בדפדפן נראתה יותר מהירה מכתיבת הבדיקות.
רציתי לכתוב בדיקות אחרי שהקוד כבר עבד, אבל אז הגיעו מה Product לבקש עוד פיצ'רים ומה QA לדווח על כל הבאגים. עד שסיימנו הכל כבר היינו שבועיים אחרי הדד-ליין.
רציתי לכתוב את הבדיקות אחרי שהפיצ'ר כבר היה באוויר ומשתמשים התחילו לדווח על באגים שאפילו ה QA לא מצאו, אבל באותו זמן עבדתי כבר על הפיצ'ר הבא וממילא הם היו צריכים תיקון כמה שיותר מהר.
אף פעם לא יהיה זמן טוב לכתוב בדיקות. עדיף להתחיל עכשיו.
ארכיטקטורת Micro Services יכולה לעזור בפיתוח מהיר יותר, בשילוב טכנולוגיות חדשות ואפילו בארגון טוב יותר של הקוד. אבל, לבנות ארכיטקטורת Micro Services מלאה למערכת לוקח זמן ופיתוח תשתיות. לא תמיד אנחנו פנויים ללכת בכל הכח על פיתוח כזה ולשבור את כל המערכת הקיימת.
אם אתם מעדיפים לעבור לפתח Micro Services בצעדים קטנים, הנה כמה הצעות לסרביסים שתוכלו להתחיל לפתח כבר היום בלי לשבור קוד קיים:
לא מזמן שיחקנו בחדר הבריחה הוירטואלי "הטנא האחרון". אחלה משחק ואני ממליץ לכם גם לקנות אם יש לכם ילדים בגיל המתאים.
לקראת סוף המשחק אחרי שפותרים את כל החידות צריך לגלות קוד סודי שמהווה חלק מ URL. בזמן שכולם היו עסוקים בחיפוש הקוד הסודי אני כמובן לא יכולתי להתאפק והעדפתי למצוא את ה URL בעצמי עם קצת עזרה מגוגל.
הסיבה שהצלחתי היא גם הסיבה לרוב בעיות אבטחת המידע שאנחנו נתקלים בהן כשאנחנו כותבים קוד: אנחנו שוכחים או לא יודעים לשאול את השאלות הנכונות. כי רק אם אני יודע שיהיה איזה פסיכי שיחפש את הקישור לא בדרך הרגילה, אני יכול להמשיך ולשאול "איך גורמים לקישור מסוים להישאר סודי". בדיוק כמו שרק אם אני יודע שיהיה איזה פסיכי שיפרסם קוד JavaScript בתוך גוף אימייל אני יכול להמשיך ולשאול "איך אני מבצע Escaping נכון לתוכן גולשים".
והדרך להגיע למצב שאנחנו יודעים את השאלות הנכונות מתחילה בלשאול את השאלות הלא נכונות, להסתכל על התשובות ועל שאלות שאנשים אחרים שואלים ולאט לאט ללמוד. לא יקרה ביום אבל התוצאה שווה את המאמץ.
שיטת העבודה המפורסמת ביותר עם גיט נקראת Git Flow. השם הקליט מסתיר שמדובר בבחירת שיטת עבודה שלא מתאימה לכל מי שעובד בגיט, שיש אופציות אחרות ושיש בה בעיות מהותיות שאולי לא מתאימות לפרויקט שלכם.
העיקרון הבעייתי מ Git Flow עליו אני רוצה להתמקד היום הוא השימוש ב Feature Branch. קודם כל אלה עיקרי המנגנון מתוך המדריך Gitflow Workflow:
רוב העבודה מתבצעת על ענף בשם Develop.
כל פיצ'ר חדש ייכתב ב Branch משלו.
כשהפיצ'ר מוכן נשלב אותו חזרה ל Develop.
מתישהו כשיש מספיק פיצ'רים או כשיעבור מספיק זמן נוציא מ Develop ענף חדש בשם Release ושם נעבוד על ייצוב הפיצ'רים ואינטגרציה.
בעבודה על פרויקטים בגישה כזאת נתקלתי ב-4 הבעיות הבאות:
אינטגרציה ארוכה ורווית קונפליקטים - כי כשיש לי שני מתכנתים שעובדים על פיצ'רים שמתנגשים, הקונפליקט בין הפיצ'רים לא יעלם אם אני אחכה שהפיצ'רים יהיו מוכנים. בדרך כלל הקונפליקטים רק יחמירו ככל שאותם מתכנתים כותבים יותר קוד, ואז כשצריכים למזג אנחנו צריכים לעדכן קוד שכתבנו לפני שבועיים-שלושה, שלא תמיד זוכרים אותו כמו שצריך.
פיצ'רים שאף פעם אין זמן לשלב אותם- כי אם אנחנו ממזגים פיצ'ר רק כשהוא מוכן אז מישהו צריך לבדוק שהפיצ'ר אכן מוכן. כאן יכול להיווצר צוואר בקבוק על ראש הצוות או ה QA ובעצם יהיו לנו פיצ'רים שיחכו הרבה זמן עד שהארגון יהיה מוכן לשלב אותם.
קשה "להתחרט" כשמגלים שפיצ'ר מסוים לא מתאים לגירסה- וזה מתחבר ל (2) כי הסיבה שאנחנו לא משלבים פיצ'ר ש"נראה מוכן אבל עדיין לא לגמרי" זה שאם הגעתי לעבוד על ה Release Branch ואני מגלה שהפיצ'ר המסוים הזה לא מספיק מוכן ורק מעכב לי את הגירסה, אין לי דרך קלה להתחרט ולהוציא אותו מ Develop או מ Release.
בזבוז זמן כתוצאה ממיזוג פיצ'ר מאוחר מדי- הרבה פעמים יהיה לי פיצ'ר תשתיתי שמשנה את איך שעובדים עם המערכת מכאן והלאה. לדוגמה פיצ'ר של "תמיכה בריבוי שפות" שמעביר את כל הטקסטים במערכת לקבצי הגדרות חיצוניים. ככל שאני מחכה עם שילוב הפיצ'ר הזה כך מתכנתים אחרים כותבים בפיצ'רים שלהם טקסטים חדשים בשיטה הישנה, ואז כשאני אגיע לשלב את הפיצ'ר התשתיתי אצטרך יותר עבודה לתקן גם את כל הפיצ'רים האחרים שנכתבו עם התשתית הישנה.
חלופה מעניינת ל Git Flow נקראת Continuous Integration. מארטין פולר כתב כהרגלו מאמר ארוך על הנושא עוד ב 2006 והוא רלוונטי גם היום. הרעיון בגדול הוא שכולם עובדים על אותו Branch ומשתמשים ב Feature Flags כדי שפיצ'רים מסוימים לא יוצגו ב UI.
כמו בהרבה מקרים בפיתוח אין פיתרון אחד שמתאים לכולם ולכל הצוותים בעולם, אבל אם גם אתם נתקלתם בבעיות בעבודה עם Branch-ים שווה להכיר ש Git Flow הוא לא הדרך היחידה לעבוד ואולי תגלו שבשיטה אחרת אתם מצליחים להתקדם מהר יותר.
נוד 16 יצא לאחרונה ואיתו המון פיצ'רים חדשים ולא מאוד מעניינים (כאן יש רשימה גדולה).
פיצ'ר אחד שתפס לי את העין ושווה לשתף הוא ההכנסה של ממשק Promises למודול השעונים. זה נראה כך:
import { setTimeout } from 'timers/promises';
async function doSomething() {
console.log('doSomething started!');
await setTimeout(2000);
console.log('Delayed Timers!');
}
doSomething();
נכון, אנחנו יודעים לכתוב setTimeout שמחזיר Promise לבד, ובכל זאת נחמד לראות דברים כאלה נכנסים לשפה והופכים לסטנדרט. המעבר ל API מבוסס Promises מלווה אותנו תקופה ארוכה ב Node ובדפדפן.
באותו הקשר ומאותה רשימה, נוד 16 קיבל גם תמיכה ב Abort Controller שזה פיצ'ר שהרבה אנשים חיכו לו ומאפשר ביטול בקשת fetch. אפשר לקרוא עליו ב MDN יחד עם דוגמה לשימוש בפיצ'ר בדפדפן בקישור כאן: https://developer.mozilla.org/en-US/docs/Web/API/AbortController.
אני לא בטוח כמה הוא יעזור ב Node.JS אבל בכל מקרה טוב שיש לטובת מי שכותבים קוד אוניברסלי.
יש כבר אימג' בדוקר האב אז הפקודה הבאה מכל מכונה תזרוק אתכם לתוך REPL של נוד 16 כדי לנסות את השטויות החדשות:
$ docker run -it --rm node:16 node