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

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

הוקס עם התניה

09/06/2021

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

function MyProjects () {
  const { data: user } = useSWR('/api/user')
  if (!user) {
    return 'loading...';
  }

  const { data: projects } = useSWR('/api/projects?uid=' + user.id)
  if (!projects) return 'loading...'

  return 'You have ' + projects.length + ' projects'
}

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

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

function MyUser() {
  const { data: user } = useSWR('/api/user')

  if (!user) {
    return 'loading...';
  }

  return (
    <MyProjects user={user} />
  );
}

function MyProjects () {
  const { user } = props;
  const { data: projects } = useSWR('/api/projects?uid=' + user.id)

  if (!projects) return 'loading...'

  return 'You have ' + projects.length + ' projects'
}

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

אופציה שניה וטובה יותר היא מה שעשו ב SWR ומה ששווה לבנות גם ב Hooks שלכם: הכנסת הלוגיקה לתוך ה Hook. ב SWR אפשר לכתוב את אותו הקוד כך:

function MyProjects () {
  const { data: user } = useSWR('/api/user')
  const { data: projects } = useSWR(() => '/api/projects?uid=' + user.id)
  // When passing a function, SWR will use the return
  // value as `key`. If the function throws or returns
  // falsy, SWR will know that some dependencies are not
  // ready. In this case `user.id` throws when `user`
  // isn't loaded.

  if (!projects) return 'loading...'
  return 'You have ' + projects.length + ' projects'
}

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

  if (typeof key === 'function') {
    try {
      key = key()
    } catch (err) {
      // dependencies not ready
      key = ''
    }
  }

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

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

סימניות

08/06/2021

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

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

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

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

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

שלוש שאלות (ותשובות) על תקיעות

07/06/2021

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

המשך קריאה

ניסיון אפקטיבי (או: מה ללמוד עכשיו)

06/06/2021

"אני מתכנת ווב. התחלתי לכתוב אתרים ב 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 בכל פלטפורמה כדי להגיע לחווית משתמש אופטימלית"


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

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

להתאמן על החיבורים

05/06/2021

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

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

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

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

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

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

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

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

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

טיפ דוקר: משימות מתוזמנות

04/06/2021

אנחנו יודעים איך להרים סרביס בדוקר עבור בסיס נתונים או שרת 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.

מתי לכתוב בדיקות

02/06/2021

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

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

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

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

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

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

איך להתחיל לשבור מונוליט למיקרו סרביסים

01/06/2021

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

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

המשך קריאה

השאלות הנכונות

31/05/2021

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

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

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

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