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

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

רגע, לא כולם משתמשים בקוברנטס???

29/08/2021

את הכותרת הבאה בהאקרניוז קלטתי מקילומטר: "עם עבודת 9 עד 5 ושני ילדים, הצלחתי לבנות את ה MVP הראשון שלי". בום. חייב לראות מה זה ומי זה ומה הקסם הסודי. שתי פיסקאות פנימה הגיעה רשימת הטכנולוגיות: Laravel, jQuery, MySQL ואיחסון על Digital Ocean. לפי המחיר של 5$ לחודש אני די בטוח שלא מדובר בקלאסטר הקוברנטס הנוצץ שלהם.

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

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

במקרה של Free Option Screener, אתר עם עיצוב מכוער הוא לא חיסרון אלא הזדמנות לשחק עם React ו AntD אחרי שאתה יודע בדיוק מה אתה רוצה לבנות. אתר שמאוחסן על VM הוא קורבן מעולה לניסוי במעבר ל Kubernetes, כי יש למה להשוות וכבר יש לך Workflow של פיתוח והעלאת גירסה.

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

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

מה ההבדל בין `docker compose` ל `docker-compose` ?

28/08/2021

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

הסיפור הרשמי של שתי הפקודות הוא פשוט - הכלי docker-compose הוא סקריפט פייתון, הוא היה שם קודם וקוד המקור שלו (אם מצאתי נכון) הוא כאן: https://github.com/docker/compose.

לעומתו docker compose מסומן בתור compose גירסה 2, כתוב ב go וקוד המקור שלו (בהנחה שמצאתי נכון) הוא כאן: https://github.com/docker/compose-cli.

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

  1. גירסה 2, כלומר docker compose תומכת ביצירת קונטיינרים בעננים של מייקרוסופט ואמזון באמצעות אינטגרציה מובנית. מספיק ליצור context, לכתוב docker compose up ויש לכם קונטיינרים באוויר ב ECS או ACI.

  2. על הלינוקס שלי, בגירסה 2 כשאני מעלה מכונות עם docker compose up ואז לוחץ Ctrl+C אני נשאר תקוע על איזה הודעת "המכונות עצרו". בגירסת docker-compose up לחיצה על Ctrl+C עוצרת את המכונות אבל גם מחזירה אותי למסוף.

  3. גירסה 1 כלומר docker-compose תומכת בכתיבת הלוג ל syslog ונראה שגירסה 2 לא. במילים אחרות קובץ קומפוז כזה:

version: '3'
services:
  worker:
    image: // image
    logging:
      driver: syslog
      options:
        syslog-address: "udp://XXX.papertrailapp.com:XXXX"
        tag: "{{.Name}}/{{.ID}}"

שמצאתי כאן בסטאק אוברפלו ובמקומות נוספים יעבוד עם docker-compose up אבל לא יעבוד עם docker compose up.

  1. קומפוז גירסה 2 (הגירסה בלי המקף) תומך ב Apple Silicon.

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

טיפ JavaScript: שדה אופציונאלי באוביקט

27/08/2021

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

const key = 'name';

const data = {
    [key]: 'bob'
};

שמחזיר את האוביקט עם המפתח name ובו הערך bob.

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

const words = ['one', 'two', 'three'];
const wordsObject = words.map(w => ({ [w]: true })).reduce((a, b) => ({ ...a, ...b }));

// now wordsObject = { one: true, two: true, three: true }

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

const s1 = getStatus(task1);
// returns { status: 'PROCESSING' }

const s2 = getStatus(task2);
// returns { status: 'DONE', result: 8 }

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

function getStatus(task) {
    return {
        status: task.status,
        ...(task.status === 'DONE' && { result: task.result }),
    };
}

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

// prints: { status: 'PROCESSING' }
console.log(getStatus({ status: 'PROCESSING', result: 7 }));

// prints: { status: 'DONE', result: 7 }
console.log(getStatus({ status: 'DONE', result: 7 }));

מדריך Vue למתחילים - חלק 7 - ניהול מידע גלובאלי

26/08/2021

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

פוסטים קודמים בסידרה:

  1. פרק 1 - פיתוח קומפוננטה ראשונה

  2. פרק 2 - תקשורת בין קומפוננטות

  3. פרק 3 - תבניות דינאמיות

  4. פרק 4 - ממשק ההרכבה Composition API

  5. פרק 5 - דוגמת פיתוח נגן YouTube ב Vue

  6. פרק 6 - יישומי Single Page Apps עם Vue Router

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

המשך קריאה

צעדים ראשונים עם grpc

25/08/2021

ספריית gRPC היא ספריית קוד פתוח מבית גוגל שתפקידה לחבר בין סרביסים במערכת שלכם בצורה יעילה ומהירה. אפשר לחשוב עליה בתור תחליף מהיר יותר ל REST APIs כאשר הרווח המרכזי של gRPC מבחינת ביצועים מגיע מהשימוש ב HTTP/2 ובשליטה טובה יותר של הספריה בפרוטוקול. מבחינת חווית משתמש הספריה מאפשרת כתיבת תוכניות מבוזרות כאילו אנחנו רצים על אותה מכונה באמצעות שימוש נרחב ב Stub-ים.

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

המשך קריאה

זהירות - קוד עובד לפניך!

24/08/2021

המדד הכי גרוע לאיכות קוד הוא "עובד/לא עובד" ואני תמיד אעדיף קוד נכון שלא עובד על פני קוד עובד ולא נכון. ועם זה הלכתי לקשקש קצת עם JavaScript היום ובפרט עם testing-library. שימו לב לבדיקה הבאה שעוברת:

it('Increases after I click the button', async () => {
  const screen = render(Counter);
  const button = screen.getByRole('button', { name: 'Click Here' });
  await userEvent.click(button);

  expect(screen.getByText(/clicked 1 times/)).toBeTruthy();
});

זה קוד בדיקה למונה לחיצות שמוודא שאחרי שלחצתי על כפתור יופיע הטקסט clicked 1 times.

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

await userEvent.click(button);

זה רעיון יפה רק ש userEvent.click לא מחזיר Promise. קוד הבדיקה הבא (שגם עובד) הוא שקול לתוכנית הראשונה:

it('Increases after I click the button', async () => {
  const screen = render(Counter);
  const button = screen.getByRole('button', { name: 'Click Here' });
  userEvent.click(button);
  await undefined;

  expect(screen.getByText(/clicked 1 times/)).toBeTruthy();
});

אה וצריך לציין - אם מוחקים את שורת ה await ונשארים רק עם זה:

it('Increases after I click the button', async () => {
  const screen = render(Counter);
  const button = screen.getByRole('button', { name: 'Click Here' });
  userEvent.click(button);

  expect(screen.getByText(/clicked 1 times/)).toBeTruthy();
});

הבדיקה נכשלת.

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

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

it('Increases after I click the button', async () => {
  const screen = render(Counter);
  const button = screen.getByRole('button', { name: 'Click Here' });
  userEvent.click(button);

  expect(screen.findByText(/clicked 1 times/)).toBeTruthy();
});

עושה לי צרבת

23/08/2021

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

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

התחושה השניה זה סיפור אחר - כשאין קליק לטכנולוגיה לוקח הרבה יותר זמן בכלל למצוא את הזמן כדי ללמוד אותה, וגם הלימוד הרבה פחות אפקטיבי. קצב ההתקדמות בטכנולוגיה כזאת יכול להיות איטי פי 2 או 3, ורק בגלל איזה רושם ראשוני או State Of Mind לא נכון.

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

חשבתי שאצליח להסתדר בלי

22/08/2021

חשבתי שאצליח להסתדר בלי לדעת עד הסוף איך z-index עובד. כל פעם שנתקעתי בבעיה איתו התיעוד היה נראה לי הרבה יותר מדי ארוך והיה לי יותר קל לחפש פיתרון ספציפי ב Stack Overflow לבעיה שלי.

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

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

אז חשבתי.

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

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

איך שולחים ומקבלים הודעות בתור RabbitMQ מתוך שרת Node.JS Express

21/08/2021

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

המשך קריאה

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

20/08/2021

בתיאוריה, תרגום פרויקט מ Docker Compose ל Kubernetes הוא הדבר הכי קל בעולם: מפעילים סקריפט אחד שמתרגם את קבצי ה docker-compose.yml לקבצי ההגדרות של k8s, מעלים את התוצאה לקלאסטר קיים ונהנים מהחיים. המציאות לקחה לי קצת יותר עבודה ודרשה מספר שינויים בקוד המערכת - אבל בצד החיובי סיימתי עם קוד טוב יותר מזה שהיה בהתחלה וגם הבנה טובה יותר של הארכיטקטורה. מהתהליך גם למדתי לחבב מספר דברים בקוברנטס, והנה השלושה המרכזיים:

המשך קריאה