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

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

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

01/06/2021

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

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

המשך קריאה

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

31/05/2021

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

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

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

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

כמה בעיות עם Feature Branches ומה אפשר לעשות במקום

30/05/2021

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

העיקרון הבעייתי מ Git Flow עליו אני רוצה להתמקד היום הוא השימוש ב Feature Branch. קודם כל אלה עיקרי המנגנון מתוך המדריך Gitflow Workflow:

  1. רוב העבודה מתבצעת על ענף בשם Develop.

  2. כל פיצ'ר חדש ייכתב ב Branch משלו.

  3. כשהפיצ'ר מוכן נשלב אותו חזרה ל Develop.

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

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

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

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

  3. קשה "להתחרט" כשמגלים שפיצ'ר מסוים לא מתאים לגירסה- וזה מתחבר ל (2) כי הסיבה שאנחנו לא משלבים פיצ'ר ש"נראה מוכן אבל עדיין לא לגמרי" זה שאם הגעתי לעבוד על ה Release Branch ואני מגלה שהפיצ'ר המסוים הזה לא מספיק מוכן ורק מעכב לי את הגירסה, אין לי דרך קלה להתחרט ולהוציא אותו מ Develop או מ Release.

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

חלופה מעניינת ל Git Flow נקראת Continuous Integration. מארטין פולר כתב כהרגלו מאמר ארוך על הנושא עוד ב 2006 והוא רלוונטי גם היום. הרעיון בגדול הוא שכולם עובדים על אותו Branch ומשתמשים ב Feature Flags כדי שפיצ'רים מסוימים לא יוצגו ב UI.

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

חדש ב Node 16 - ממשק Promises ל Timers

29/05/2021

נוד 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

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

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

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