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

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

למה צריך שורת פקודה כשיש GUI? (טיפ AWS)

11/06/2021

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

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

הפקודה:

$ aws dynamodb list-tables

מדפיסה JSON עם רשימת כל הטבלאות:

{
    "TableNames": [
        "Table1",
        "Table2",
        "Table3"
    ]
}

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

 AWS_PAGER="" aws dynamodb list-tables | jq '.TableNames[]'
"Table1"
"Table2"
"Table3"

ונמשיך עם xargs בשביל למחוק אחת אחת:

$ AWS_PAGER="" aws dynamodb list-tables | jq '.TableNames[]'| xargs -n 1 aws dynamodb delete-table --table-name

האם היה נחמד לקבל ממשק GUI יותר נוח מאמזון? ברור. ועדיין עדיף ללמוד לעבוד עם ה CLI מאשר לבזבז שעה על מחיקת אוביקטים וניווט ב GUI.

אונליין ואופליין

10/06/2021

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

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

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

והאופליין? כמות המיטאפים שמתוכננים החודש עם הכותרת "מפגש פרונטאלי ראשון אחרי הקורונה" וכמות האנשים שבאים אליהם מזכירה לנו שהרבה יותר קל לנו ליצור קשרים עם אנשים כשאנחנו איתם באותו מרחב פיזי, ושאנשים רבים התגעגעו לחוויה הזאת ומחפשים אותה. אם גם לכם זה היה חסר שווה להעיף מבט בלינק https://www.meetup.com/cities/il/tel_aviv-yafo/events/ ולראות אם יש אירוע שמעניין אתכם ומסתדר בשעות.

הוקס עם התניה

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 לא מצאו, אבל באותו זמן עבדתי כבר על הפיצ'ר הבא וממילא הם היו צריכים תיקון כמה שיותר מהר.

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