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

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

ברגע שזה אפשרי

28/05/2021

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

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

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

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

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

  2. ולמי בכלל יש זמן לזה?

  3. ויש לי טסט לעוד שלושה חודשים חבל לא לנצל אותם.

  4. נו ובטח באותו יום שאקנה את האוטו מישהו יקנא וישרוט לי אותו.

  5. וממילא למכוניות יש ירידת ערך מטורפת עדיף לחכות עוד שנה.

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

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

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

שאלות לראיונות עבודה: הפקודה throw ב Node.JS

27/05/2021

במדריך Hello World של RabbitMQ הם הציגו את הקוד הבא בתור דוגמה לקוד ששולח הודעה לתור הודעות:

var amqp = require('amqplib/callback_api');

amqp.connect('amqp://localhost', function(error0, connection) {
  if (error0) {
    throw error0;
  }
  connection.createChannel(function(error1, channel) {
    if (error1) {
      throw error1;
    }
    var queue = 'hello';
    var msg = 'Hello world';

    channel.assertQueue(queue, {
      durable: false
    });

    channel.sendToQueue(queue, Buffer.from(msg));
    console.log(" [x] Sent %s", msg);
  });
});

דני דפדפני רצה לשים את הקוד בתוך פונקציה ולהפעיל אותה בתוך try/catch כדי להציג הודעת שגיאה יפה יותר אם החיבור לתור נכשל, אז הוא עדכן את הקוד שיראה כך:

var amqp = require('amqplib/callback_api');

function doSend() {

    amqp.connect('amqp://localhost', function(error0, connection) {
    if (error0) {
        throw error0;
    }
    connection.createChannel(function(error1, channel) {
        if (error1) {
        throw error1;
        }
        var queue = 'hello';
        var msg = 'Hello world';

        channel.assertQueue(queue, {
        durable: false
        });

        channel.sendToQueue(queue, Buffer.from(msg));
        console.log(" [x] Sent %s", msg);
    });
    });
}

try {
    doSend();
} catch (err) {
    console.log('Sorry connection failed. Try again next year');
}

ולשאלות:

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

  2. למה היא לא עבדה?

  3. איך הייתם מעדכנים את הקוד, בלי לשנות את קוד הפונקציה doSend או החתימה שלה, כדי להציג את הודעת השגיאה של דני?

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

אפשר לפרסם את התשובות שלכם בתגובות או אם אתם לא בטוחים שווה לקרוא את המדריך הזה כדי ללמוד איך לטפל בשגיאות ב Node.JS.

טיפ דוקר: שימוש ב Access Tokens במקום בסיסמאות

26/05/2021

אם אתם כותבים אימג'ים ומעלים אותם ל Docker Hub וודאי יש לכם כבר חשבון ב Docker Hub וכבר חיברתם את כלי שורת הפקודה לחשבון עם docker login. מה שלא בטוח ששמתם לב זה ש docker login יצר קובץ בשם config.json בתוך תיקיית .docker בתיקיית הבית שלכם, ובתוכו דוקר כתב ב Plain Text את הסיסמה שלכם לדוקר האב.

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

מה שכן אפשרי ושווה את המאמץ זה להשתמש במשהו שנקרא Personal Access Token במקום סיסמה לדוקר האב. השימוש ב Personal Access Token מאובטח (קצת) יותר מסיסמה בגלל ש:

  1. אפשר לראות דרך ממשק הניהול שלהם איזה פעולות נעשו דרך ה Access Token, ואם היה משהו חשוד אז לבטל.

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

  3. בחיבור דרך Access Token אי אפשר לעשות פעולות ניהוליות בחשבון כמו החלפת סיסמה.

יצירת ה Access Token היא ממש פשוטה - נכנסים לממשק הניהול של דוקר האב, בוחרים בטאב Security ולוחצים New Access Token. בשביל להתחבר עם ה Access Token מפעילים docker login וכשדוקר מבקש את הסיסמה אנחנו מדביקים את ה Access Token במקום.

והתוצאה: יש לנו קובץ config.json בתיקיית ~/.docker שמכיל את אותו Access Token במקום את הסיסמה. שמרו עליו טוב.

מבלבל בכוונה

25/05/2021

מחוץ לקונטקסט, כשאנחנו לא בעניינים, הרבה פיצ'רים מרגישים מבלבלים בכוונה. למה Dockerfile צריך את הפקודה EXPOSE? או VOLUME? למה ריאקט היה צריך את componentWillMount (שלשמחתנו כבר בוטל)? למה typeof null צריך להיות object ב JavaScript? ולמה HTML Audio צריך לקבל ילדים מכל מיני פורמטים, כשיש פורמט אחד שכל הדפדפנים יודעים לנגן?

היו רגועים. אף אחד לא ניסה לבלבל אתכם.

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

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

רק ככה אפשר להבין את כל הפינות של השפה, כולל אלה שנראות לגמרי נטולות הגיון.

גם המוח שלכם עושה את זה?

24/05/2021

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

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

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

  1. אולי רק אבדוק כמה דקות מה חדש ב ynet? (ביי ביי 10 דקות יקרות)

  2. אולי יש ספריה שמישהו כבר כתב שפותרת את זה? (ביי ביי 5 שעות שלעולם לא יחזרו)

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

  4. אולי אני בתחום הלא נכון? (ביי ביי חצי שעה על מחשבות חסרות ערך)

  5. בחברה הלא נכונה?

  6. בחיים הלא נכונים?

  7. מה קורה עם ההתחממות הגלובאלית עכשיו?

  8. רגע כבר זמן לאכול צהריים? ערב? לסגור את המחשב?

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

סטאקבליץ, נוד ו Web Assembly

23/05/2021

סטאקבליץ הודיעו לאחרונה שהצליחו לקמפל את Node.JS בתוך Web Assembly ועל הדרך חיברו את התקני הרשת שלו ל Service Worker API. הנה מה שזה אומר בשבילנו ובקצרה:

  1. סטאק בליץ בונים סביבת פיתוח בתוך הדפדפן, קצת כמו VS Code רק אונליין.

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

  3. קימפול נוד בתוך דפדפן אומר שדפדפן כרום שלכם מריץ Node.JS. לא שרת מרוחק שמריץ ואנחנו מתחברים אליו אלא ממש הדפדפן עצמו. התוצאה היא תוכניות שרצות מאוד מהר כי הכל קורה מקומית.

  4. אבל זה לא נגמר שם - כי כשאתה מחבר את קווי הרשת של Node.JS ל ServiceWorker API אתה בעצם משתלט על התקשורת היוצאת מהדפדפן: כל בקשת רשת שיוצאת מהדפדפן עוברת קודם דרך אותו Node.JS פנימי שרץ אצלי בתוך הדפדפן. זה אומר שדפדפן יכול "לפנות" לשרת שרץ גם הוא בתוך הדפדפן, ובאופן אוטומטי ה Service Worker יתפוס את הבקשה לפני שהיא יוצאת מהדפדפן ויעביר אותה לאותו תהליך Node.JS פנימי שרץ באותו דפדפן עצמו.

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

  6. וכאילו שזה לא מספיק, בזכות השימוש ב PWA בתוך סטאקבליץ יש כפתור "Install" שמאפשר להתקין אותם בתור אפליקציית Desktop.

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

הקישור הזה (לפתוח מכרום בלבד) ייצור גם לכם כזה שרת בדפדפן: https://stackblitz.com/fork/nextjs

שווה לנסות. העתיד של הרשת מעולם לא נראה מעניין יותר.

לא מאמין שפספסתי את זה 3 פעמים כבר

21/05/2021

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

מאז חזרתי לפחות עוד פעמיים לתיעוד, כל פעם כשהייתי צריך לבנות משהו אחר. פעם אחת בשביל להבין בדיוק איך Image עובד או למה האימג'ים שלי יוצאים כל כך גדולים ופעם נוספת כדי ללמוד על Volumes ואיך לנהל Persistency בתוך docker-compose.

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

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

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

שתי תוכניות פעולה

20/05/2021

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

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

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

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

מה זה Serverless ולמה שיהיה לכם אכפת

19/05/2021

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

הנה הקוד רק בשביל שיהיה לנו קל לדבר:

const AWS = require("aws-sdk");

const dynamo = new AWS.DynamoDB.DocumentClient();

exports.handler = async (event, context) => {
  let body;
  let statusCode = 200;
  const headers = {
    "Content-Type": "application/json"
  };

  try {
    switch (event.routeKey) {
      case "GET /items":
        body = await dynamo.scan({ TableName: "Leads" }).promise();
        break;
      case "PUT /items":
        let requestJSON = JSON.parse(event.body);
        await dynamo
          .put({
            TableName: "Leads",
            Item: {
              id: requestJSON.email,
              name: requestJSON.name,
              email: requestJSON.email,
            }
          })
          .promise();
        body = `Put item ${requestJSON.email}`;
        break;
      default:
        throw new Error(`Unsupported route: "${event.routeKey}"`);
    }
  } catch (err) {
    statusCode = 400;
    body = err.message;
  } finally {
    body = JSON.stringify(body);
  }

  return {
    statusCode,
    body,
    headers
  };
};

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

הנה כמה דברים שהפכו את גישת ה Serverless למדליקה עבור הפרויקט הזה:

  1. הכל היה באוויר תוך חצי שעה. בלי Deployment, בלי לבנות Database, בלי להגדיר משתמשים ומה מותר למי ומה אסור. חמישים שורות ולדרך.

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

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

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

  5. לא צריך לכתוב Dockerfile ו docker-compose.yml. שתיים-שלוש לחיצות ב GUI או מספר פקודות מה CLI מספיקות כדי לחבר את כל החוטים.

בצד החסרונות שווה לשים לב ש:

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

  2. צריך לעמוד בפיתוי ולא להדביק את הקוד לתוך הממשק הגרפי אלא ליצור Pipeline מסודר של גיטהאב לבניה ו Deployment.

  3. התיעוד של AWS ארוך מדי וכולל המון מידע לא רלוונטי.

  4. אין לי מושג כמה זה יעלה בסוף.

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