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

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

מרגיש לי מסוכן

06/05/2022

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

  1. להוריד את תחושת הסיכון.

  2. לעבור להסתכל על הסיכוי.

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

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

  2. אפשר לספר על ההוא שזכה והחיים שלו עכשיו השתנו מקצה לקצה.

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

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

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

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

טיפ JavaScript: שמירת מידע לקבצים וטעינה חזרה

05/05/2022

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

המשך קריאה

חצי שעה ביום

04/05/2022

בחצי שעה ביום אפשר ללמוד שפה חדשה (או שפת תכנות חדשה).

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

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

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

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

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

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

להבין שיש בעיה

03/05/2022

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

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

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

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

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

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

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

בואו נכתוב מסוף עם Python, React ו MobX

02/05/2022

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

המשך קריאה

מה זה Tearing ב React ולמה שיהיה לכם אכפת

01/05/2022

גירסה 18 של ריאקט הכניסה לשימוש מנגנון חדש שנקרא Concurrent Mode. אם אתם בונים אפליקציית ריאקט חדשה ב Vite או create-react-app, המנגנון מופעל כברירת מחדל. אם אתם משדרגים אפליקציה ישנה לריאקט 18 אתם תקבלו הודעה שמבקשת מכם להחליף את פקודת ה ReactDOM.render בפקודה בשם ReactDOM.createRoot כדי להפעיל את המנגנון. מנגנון ה Concurrent Mode אמור לעזור לפתור בעיות ביצועים שנובעות מ render-ים ארוכים מדי.

מנגנון Concurrent Mode מוסיף פונקציה בשם startTransition שמאפשרת לנו לסמן שעדכון מסוים הוא חשוב וצריך להפסיק render-ים פחות חשובים ומהר מהר לעשות render חדש רק בשביל השינוי הזה. לכאורה פיצ'ר נחמד ולא מזיק שאפילו יכול לעזור לביצועים, אבל האמת קצת יותר מורכבת והיא עלולה לקפוץ עליכם בהפתעה.

את המושג tearing בהקשר של בעיית UI לא מצאתי בתיעוד של ריאקט או בהכרזה על ריאקט 18, אלא רק מפוסטים אחרים שדיברו על הבעיה והרצאה מצוינת ביוטיוב של Daishi Kato. אם יש לכם 20 דקות פנויות שווה להקשיב לו: https://www.youtube.com/watch?v=oPfSC5bQPR8.

בחזרה ל Tearing - בהקשר של ריאקט המושג מתאר מצב בו Concurrent Mode גורם ל UI להיות לא קונסיסטנטי בגלל שינוי משתנים גלובאליים. הבעיה היא כזאת:

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

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

  3. תוך כדי ה render היותר דחוף, הערך של המשתנה הגלובאלי משתנה.

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

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

דאישי הראה בהרצאה דוגמה מעניינת, אחרי שצמצמתי אותה קצת הגעתי לקוד הזה שממחיש בדיוק את הבעיה:

import { useState, useTransition } from 'react'

let lastMouseX = 0;

window.addEventListener('mousemove', (e) => {
  lastMouseX = e.offsetX;
});

function MouseTracker() {
  const start = performance.now();
  while (performance.now() - start < 20);

  return (
    <p>{lastMouseX}</p>
  );
}

function App() {
  const [count, setCount] = useState(0)
  const [isPending, startTransition] = useTransition();

  return (
    <div className="App">
      <p>isPending = {JSON.stringify(isPending)}</p>
      <button onClick={() => {
        startTransition(() => {
          setCount(c => c + 1)
        });
      }}>{count}</button>
      <MouseTracker />
      <MouseTracker />
      <MouseTracker />
      <MouseTracker />
      <MouseTracker />
      <MouseTracker />
    </div>
  )
}

export default App

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

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

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

בגירסא 18 של ריאקט, אם מוסיפים קריאה ל startTransition כמו שהוספתי בדוגמה, הדפדפן עוצר את הרינדור באמצע כדי לטפל בלחיצה על הכפתור. זה נותן לדפדפן הזדמנות לטפל באירוע mousemove באמצע ה render ולעדכן את המשתנה הגלובאלי. התוצאה היא ש-6 המופעים של MouseTracker לא מציגים את אותו ערך.

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

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

קוד שאני לא רוצה לכתוב

30/04/2022

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

ככל שהתקדמתי שורה ועוד שורה חשבתי -

״למה אני צריך לכתוב את זה? בטח מישהו כבר כתב את זה קודם״

״חייבת להיות ספריה שעושה את זה... אני לא מאמין שאני מבזבז ככה את הזמן״

״יו אני בטח מפספס פה מלא מקרי קצה, בחיים לא אצליח לתפוס ולתקן את כולם״

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

״עוד פעם if ??? זה כבר החמישי שלי בפונקציה. למה כל כך הרבה דברים יכולים להשתבש!?״

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

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

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

מובאקס - מעבר לבייסיקס

29/04/2022

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

  1. מובאקס בעשר דקות

  2. איך לנהל State גלובאלי של יישום ריאקט עם MobX

  3. וובינר מבוא למובאקס

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

המשך קריאה

איך ללמוד כל טכנולוגיה ממש מהר

28/04/2022

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

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

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

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

בעיות לא גדולות

27/04/2022

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

  1. כל פעם שמעלים גירסה יש כמה שניות של Down Time בהן המערכת לא זמינה.

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

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

  4. יש כמה בדיקות שמדי פעם נכשלות בלי שאף אחד יודע למה.

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

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

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