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

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

בוא ננסה

21/11/2023

המשפט שאני הכי אוהב להגיד, והכי לא אוהב לשמוע.

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

לא היית רוצה לשמוע "אולי זה יעבוד" מהאינסטלטור כשיש סתימה בצנרת.

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

בוא ננסה. אולי זה יעבוד.

אבל זה עובד

20/11/2023

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

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

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

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

19/11/2023

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

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

המשך קריאה

יציאה מלולאה בסקאלה

18/11/2023

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

while True:
    line = input()
    print(line)
    if line == "stop":
        break

בואו ננסה לתרגם אותה לסקאלה.

המשך קריאה

היום שאחרי

17/11/2023

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

איפה אהיה בעוד חמש שנים?

על איזה פרויקטים אעבוד?

האם זה עדיין יעניין אותי?

הבעיה עם לא לחשוב על היום שאחרי היא ש"היום שאחרי" יגיע בין אם נרצה או לא.

ויותר קל לבנות את החיים הטובים ביום שאחרי כשמתחילים לתכנן מהיום.

שימו לב: הגדרת משתני סביבה בתחילת פקודה

16/11/2023

רוב ה Shell-ים תומכים בכתיב של הגדרת משתני סביבה לפני פקודה ובגלל זה אפשר לכתוב:

FOO=10 bash -c 'echo $FOO'

ולקבל על המסך את המספר 10.

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

RUN apt-get update && apt-get install cowsay

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

RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install cowsay

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

FOO=10 bash -c 'echo $FOO' && bash -c 'echo $FOO'

הפעם השורה תדפיס 10 רק פעם אחת, למרות שפקודת ההדפסה מופיעה פעמיים.

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

RUN DEBIAN_FRONTEND=noninteractive apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install cowsay

אפשרות קצת יותר נוחה היא להוסיף אותו בשורת ENV לפני הרצת הפקודה:

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install cowsay

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

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 כי אולי אנשים לא מכירים את הספריה הספציפית שבחרת ובשביל מה להכניס עכשיו עוד תלות. גם אני לפעמים מפשל בזה.

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

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