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

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

בואו נבנה לוח מודעות ב Rails בלי JavaScript

29/01/2023

אחת הדוגמאות הראשונות שאני מראה ל Web Sockets היא בניה של לוח מודעות משותף לכמה גולשים - כל פעם שגולש אחד שולח הודעה, ההודעה מיד מופיעה על המסך של כל האחרים. והיום ננסה לבנות את אותו לוח מודעות בלי לכתוב שורה של JavaScript, רק באמצעות המנגנונים המובנים ב Rails.

המשך קריאה

מעקפים

28/01/2023

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

function isInCanvas(x, y) {
    if (typeof x === 'string') {
        x = Number(x);
    }
    if (typeof y === 'string') {
        y = Number(y);
    }

    // check if (x,y) is in the canvas
}

וכן יש שתי בעיות עם מעקפים:

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

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

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

פיצ'ר חשוב אבל לא בשבילי

27/01/2023

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

אבל אם באותו React את לא יודעת מה זה Server Side Rendering או למה להשתמש ב useDeferredValue, לא קרה כלום. זה אולי יהיה מכשול בראיונות עבודה אבל בחיים האמיתיים יש הרבה פרויקטים שלא צריכים את הפיצ'רים האלה, ויותר מזה - יש הרבה פרויקטים בהם בגלל שינסו להטמיע את הפיצ'רים האלה הפרויקט רק יהפוך ליותר קשה לתחזוקה.

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

את הטעות הזאת אפילו TypeScript לא הצליח לתפוס

26/01/2023

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

function replaceByDictionary(text: string, replacements: Record<string, string>) {
    let interpolatedText = text;

    for (const [key, value] of Object.entries(replacements)) {
        interpolatedText = interpolatedText.replace(key, value);
    }
}

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

const replacedText = replaceByDictionary("I love TypeScript", { "I": "Everyone" });
await fetch(`https://tocode.requestcatcher.com/test?text=${replacedText}`, { method: 'POST' });

רק כדי להישאר עם לב (וקוד) שבור כשגיליתי שהבקשה שתכל'ס נשלחה לשרת היתה עם ה URL:

POST /test?text=undefined

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

הפיתרון? כמו תמיד בתכנות אין פיתרונות קסם, אבל כן יש שתי אפשרויות מרכזיות:

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

const replacedText: string = replaceByDictionary("I love TypeScript", { "I": "Everyone" });

ועל זה טייפסקריפט כבר יצעק.

או (ולדעתי עדיף) להחליט להוציא לפונקציה את הקוד ששולח טקסט לשרת, ואז נקבל:

async function postText(text: string) {
    await fetch(`https://tocode.requestcatcher.com/test?text=${replacedText}`, { method: 'POST' });
}

const replacedText = replaceByDictionary("I love TypeScript", { "I": "Everyone" });
await postText(replacedText);

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

שני סוגים של קוד נכון

25/01/2023

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

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

FROM postgres:15.1

במקום:

FROM postgres

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

או ב JavaScript נעדיף לכתוב:

JSON.parse(data);

על פני:

eval('(' + data + ')');

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

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

ורסל מציעה את הדרך הקלה ביותר להעלות יישום Flask לענן

24/01/2023

ורסל (vercel) מפורסמת בזכות הפריימוורק next.js, אותה יצר אותו גיירום ראוך שהקים גם את ורסל. אבל חוץ מפרויקטי ריאקט לורסל יש עוד לא מעט פיצ'רים בשרוול. הם חולמים להיות שכבה מעל AWS שתאפשר הקמה קלה יותר של שירותים מבוססי ענן, ולפחות לפי התקנת יישום flask על התשתיות שלהם - הם מקיימים ובגדול. בואו נראה איך זה עובד.

המשך קריאה

הפיטורים בטק (או: מה עושים עם הזמן הפנוי)

23/01/2023

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

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

המשך קריאה

טיפ לינוקס: כניסה לסרביסים באמצעות SSH Tunnel

22/01/2023

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

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

בואו נראה דוגמה עם redis - אני מקים שתי מכונות Ubuntu, מתקין redis על שתיהן ומייצר חיבור ssh ביניהן כך שאפשר יהיה ממכונה אחת להתחבר ב ssh למכונה השניה. כתובות ה ip שלי הן 192.168.64.15 ו 192.168.64.13.

קודם כל אני נכנס למכונה 13 ושם מכבה את שרת ה redis כך שחיבור מקומי ייכשל (כדי לא להתבלבל):

$ systemctl stop redis-server
$ redis-cli
Could not connect to Redis at 127.0.0.1:6379: Connection refused

אני גם מריץ את הפקודה הבאה כדי לוודא שיש לי גישת ssh ממכונה 13 למכונה 15:

ssh ubuntu@192.168.64.15 /bin/true

עכשיו נכנס למכונה 15 ושם מוודא שיש לי חיבור ל redis המקומי. אגדיר שם גם מפתח x לדוגמה עם הערך 10:

$ ssh ubuntu@192.168.64.15
$ redis-cli
redis-cli
127.0.0.1:6379> set x 10
OK

ועכשיו מגיעים לקסם. במכונה 13 אני מפעיל:

$ ssh -L 6379:localhost:6379 ubuntu@192.168.64.15

המספר 6379 הוא הפורט של רדיס. אני כותב אותו פעמיים כדי שחיבורים במכונה המקומית ל 6379 יחוברו אוטומטית לפורט 6379 במכונה השניה. הפקודה גורמת להפעלת SSH Session אז אל תופתעו כשאתם מוצאים את עצמכם בתוך shell במכונה 15.

נפתח את מכונה 13 בחלון נוסף ושם אני כותב:

$ redis-cli

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

127.0.0.1:6379> get x
"10"

כלומר הפקודה redis-cli שרצה על מכונה 13 מתנהגת כאילו על המכונה המקומית בפורט 6379 יש שרת redis, ובצד השני החיבור נראה כאילו הוא מגיע מהמכונה המקומית ולכן למרות ש redis מקשיב רק לממשק הרשת המקומי עדיין הצלחנו להתחבר אליו.

שידרוג וובפאק בתיקיית הדוגמאות בקורס ריאקט

21/01/2023

אחד הדברים שאני אוהב בקורס ריאקט פה באתר הוא היצירה של פרויקט מאפס בכל הדוגמאות, כאשר לכל דוגמה יש קובץ webpack.config.js משלה. אני יודע שבעולם האמיתי הרבה פעמים תשתמשו ב vite או create-react-app כדי ליצור את תבנית הפרויקט, ואני אפילו מלמד את הכלים האלה במסגרת הקורס, אבל גם מאמין שכשהדוגמאות פשוטות ואפשר להיכנס לכל קבצי ההגדרות ולהבין מה כל שורה שם זה עוזר להבין איך דברים עובדים.

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

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

לאחרונה גיליתי בדרך הקשה ש Node הוסיפו משהו שהתנגש עם webpack 4, זה שבר כמה דברים ב create-react-app וכן גם את הגדרות הפרויקט הקטנות שלי. יש פרטים בקישור הזה. בכל מקרה כשדברים נשברים זאת תמיד הזדמנות טובה לשדרג.

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

devServer: {
  overlay: true,
},

ל:

devServer: {
  client: {
    overlay: true,
  }
},

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

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

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

מצאו את המתחזים!

20/01/2023

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

ואני מודה, גם אני פעם נפלתי בבורות האלה.

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

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

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

  1. ספרי על הפרויקט האחרון שבנית.

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

  3. איזה פיצ'ר היה לך קשה לממש ולמה.

  4. איזה פיצ'ר היית בטוחה שהולך טוב, אבל נשבר בפרודקשן? מה קרה שם?

  5. ספרי על תקלת פרודקשן מעניינת שטיפלת בה.

  6. איך נראה תהליך פיתוח תוכנה אידאלי בינייך?

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