שלום אורח התחבר

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

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

מעדיפים לקרוא מהטלגרם? בקרו אותנו ב:@tocodeil

או הזינו את כתובת המייל וקבלו את הפוסט היומי בכל בוקר אליכם לתיבה:

הנה קוד JavaScript קצר ולגמרי מומצא שתפקידו לספור ביקורים באתר. כל פעם שאתם מגיעים לאתר עם הקוד הזה משתנה ב local storage יעלה ב-1, ויוצג על המסך בתוך ה body:

const visits = localStorage.getItem('visits');
if (visits == null) {
  localStorage.setItem('visits', 1);
} else {
  localStorage.setItem('visits', Number(visits) + 1);
}

document.body.textContent = Number(visits) + 1;

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

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

ככה זה נראה אחרי תיקון:

let visits = localStorage.getItem('visits');

if (visits == null || isNaN(Number(visits))) {
  visits = 1;
} else {
  visits = Number(visits) + 1;
}

localStorage.setItem('visits', visits);
document.body.textContent = visits;

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

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

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

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

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

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

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

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

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

טיפ שעזר לי בבניית Micro Services טובים הוא להשאיר כמה שיותר נקודות התערבות, ולשמור כמה שפחות State. לדוגמא - אם יש לי סרביס שליחת מייל תזכורת שפונה לסרביס אחר כדי לקבל ממנו רשימת לקוחות, אני ארצה לבנות את סרביס המיילים עם שתי נקודות כניסה: ב Default הוא ייקח את רשימת הלקוחות מהסרביס שמחזיק את הלקוחות, אבל נקודת כניסה נוספת תאפשר הפעלה במעין Debug Mode, כאשר רשימת הלקוחות עוברת כפרמטר. בצורה כזאת אנחנו יכולים לבדוק כל אחד מהסרביסים בנפרד ולנסות שוב ושוב להריץ רק את הלקוחות שנכשלו.

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

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

  1. נכתוב Decorator שיריץ את הפונקציה שלנו בלולאת while שמוציאה אלמנטים מתור וממשיכה להפעיל את הקוד שוב ושוב עד שהתור מתרוקן.

  2. נכתוב פונקציה בשם recur שתדמה את הקריאה הרקורסיבית. בעצם מה שהפונקציה תעשה זה בסך הכל להוסיף את הפרמטרים שהעברנו לה לתור מה Decorator.

  3. נפעיל את recur במקום את הקריאה הרקורסיבית, אבל עם אותם פרמטרים ובלי להשתמש במידע שחוזר.

קוד להמחשה? ברור-

def tco(f):
    q = []
    def recur(*args):
        q.append(args)

    def inner(*args):
        q.append(args)
        while len(q) > 0:
            next_call_args = q.pop()
            res = f(*next_call_args)
        return res

    inner.recur = recur
    return inner

@tco
def fact(n, start=1):
    if n <= 1:
        return start

    return fact.recur(n - 1, start * n)

print(fact(1_000))

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

def fact(n, start=1):
    if n <= 1:
        return start

    return fact(n - 1, start * n)

והיתה נופלת בחישוב על Stack Overflow אם הייתם מנסים לחשב עצרת של מספר גדול (למשל אלף). בשביל להוסיף אופטימיזציית TCO כמעט ולא צריך שינויים: מספיק היה להוסיף את ה Decorator ולהחליף את הקריאה ל fact בקריאה ל fact.recur.

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

איתמר קרמר כתב לאחרונה את הדברים הבאים:

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

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

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

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

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

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

  1. כמה טכנולוגיות חדשות למדתם בשנתיים האחרונות?

  2. האם אתם מרגישים שאין מספיק הזדמנויות טובות בתעשיה?

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

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

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

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

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

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

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

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

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

שבועיים אחר כך, ב 4.6 אעביר כאן וובינר על Service Worker. זה API מאוד משמעותי ב Web שיש לו תמיכת דפדפנים רחבה ואתרים רבים נעזרים בו כדי להציע תוכן Offline ולשפר ביצועים. אנחנו נראה 2-3 דוגמאות לכתיבה ועבודה עם Service Worker.

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

רישום לוובינר Service Worker: https://www.tocode.co.il/workshops/98

רישום לוובינר Github Actions: https://www.tocode.co.il/workshops/97

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

בעולם ה Web הפריימוורקים המרכזיים: React, Vue ו Angular עומדים כולם על קרקע יציבה. ב React אחרי הכניסה של Hooks כל ה API התייצב והאקוסיסטם נבנית לאט לאט סביב קונספט זה. נכון, עוד מעט נראה את Concurrent Mode בפרודקשן, אבל זה רחוק מלהיות מהפכה. אנגולר כבר בגירסא 9 אבל מאז גירסא 2 אין שינויים מהפכניים ו Vue3 כבר זמינה בגירסת בטא וכנראה תישאר איתנו תקופה ארוכה.

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

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

וזה מעולה.

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

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

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

המשך קריאה...

כן, כדאי לבדוק כמה שיותר את המערכת בסביבת בדיקות.

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

כן, כדאי שיהיה לכם סט חזק של בדיקות אוטומטיות.

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

מה עושים?

  1. משתמשים ב Load Balancer כדי להגיש את הגירסא החדשה רק לחלק קטן מהמשתמשים בשבוע הראשון (Haproxy, ACL ועוגיות יהיו חברים טובים שלכם כשמגדירים את זה).

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

  3. לוקחים בחשבון תיקוני באגים מ Production בלוחות הזמנים של כל סבב (אם הקודם הסתיים בהעלאת גירסא).