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

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

הדף הכי חשוב בתיעוד (שברוב המקרים בכלל לא כתוב)

15/11/2023

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

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

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

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

הקושי לזהות בשר מקולקל

14/11/2023

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

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

"אני לא רוצה שזה יהיה מקולקל" לא יהפוך אותו לטרי.

היום למדתי: פונקציה שמחזירה פונקציה בסקאלה

13/11/2023

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

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

const add = x => y => x + y;

(שגם בשעתו נראה לי כמו קסם אבל היום כבר הגיוני לגמרי).

ובפייתון אנחנו כותבים:

def add(x):
    return lambda y: x + y

מקבל בסקאלה קיצור דרך כבר ברמת החתימה של הפונקציה:

def add(x: Int)(y: Int): Int = x + y

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

def add2(x: Int): (y: Int) => Int = { y =>
  x + y
}

אבל זה פשוט פחות יפה.

כשדברים פשוטים דורשים הרבה קוד

12/11/2023

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

//...
  const [loading, setLoading] = useState<boolean>(false);
  const [data, setData] = useState<Item[] | null>(null);
  const [error, setError] = useState<Error | undefined>(undefined);

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);

      try {
        const response = await fetch("/api/users");

        if (!response.ok) {
          const error = await response.json();
          throw new Error(`Error: ${error.error || response.status}`);
        }

        const data = await response.json();
        setData(data);
      } catch (e) {
        setError(e as Error);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, []);

//...

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

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

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

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

חמש דוגמאות קצרות לשימוש ב cat effect בסקאלה

11/11/2023

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

מסיבה זאת נוצרו הרבה ספריות הרחבה לשפה שמאפשרות לייצר חישוב עתידי בצורה פונקציונאלית וללא Side Effects. אחת הפופולריות היא cats effect והנה חמש דוגמאות ראשונות שכתבתי עם הספריה.

המשך קריאה

סוף הדרך

10/11/2023

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

  1. תשתיות (תלויות, בסיסי נתונים, שרתים) ישנות. למעשה כל כך ישנות שאין אומץ או יכולת לשדרג אותן.

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

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

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

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

אופס הכנסתי קונסטריינט

09/11/2023

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

def createPost(g: GraphTraversalSource, slug: String, title: String, publishedAt: String): Vertex =
  g.addV("post")
    .property("slug", slug)
    .property("title", title)
    .property("publishedAt", LocalDateTime.parse(publishedAt, formatter))
    .next()

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

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

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

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

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

קוד מושלם

08/11/2023

אם הייתי יודע איך יראו כל הדרישות העתידיות מהמערכת הייתי שמח לכתוב את הקוד המושלם.

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

יש לי שלושה ימים עד הראיון עבודה בריאקט - מה ללמוד?

07/11/2023

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

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

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

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

  4. הרבה אנשים כן דיברו על החשיבות של פיתוח Front End באופן כללי - אם זה טייפסקריפט, היכרות טובה עם JavaScript ויכולות חדשות של השפה, ואפילו CSS ויכולות חדשות שלו.

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