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

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

אלא אם כן אתם נטפליקס

21/12/2024

כשגולש נכנס אליכם לאתר, כמה זמן הדפדפן שלו צריך לעבוד ולהריץ JavaScript לפני שהוא יכול להשתמש באתר? מסתבר שבממוצע התשובה תלויה הרבה יותר בטכנולוגיה איתה בחרתם לבנות את האתר מאשר בפיצ'רים של האתר עצמו. הלכתי ל Page Speed לבדוק 9 אתרי ריאקט ו next.js ולא הופתעתי מהתוצאות:

  1. בשביל לקרוא מאמר מהאתר של vercel צריך להריץ JavaScript במשך 2.5 שניות.

  2. דף הבית של nike העסיק את הדפדפן במשך 8.1 שניות רק בהרצת הקוד.

  3. דף הבית של solana עם הסלוגן Poweful for developers, Fast for everyone בילה "רק" 1.6 שניות בהרצת קוד.

  4. לפני שבוחרים ארוחה ב wegmans הדפדפן צריך לבלות 10 שניות בהרצת ה JavaScript שלהם.

  5. לפני שבוחרים קורס באקדמיה של קאן צריך לחכות 2.9 שניות כדי להריץ את ה JavaScript אצלם בעמוד.

  6. לפני שאפשר לקרוא עדכונים ברדיט נקדיש 2.3 שניות להרצת JavaScript.

  7. בקורסרה נבלה 3.8 שניות בהרצת ה JavaScript לפני שנוכל להיכנס לקורסים.

  8. יודמי טיפה יותר טובים עם 3.1 שניות זמן ריצה של JavaScript בכניסה לעמוד.

צריך להגיד - משתמשים לא רוצים לבלות זמן בהמתנה ל JavaScript שירוץ. אם אפשר היה לבנות את האתר בצורה שתתן את אותה פונקציונאליות אבל בלי 10 שניות של זמן ריצה של JavaScript כולם היו שמחים יותר. מה שמשותף לכל האתרים ברשימה הוא הבחירה ב React, חלקם ישירות וחלקם גם עם next.js. כן יהיה מעניין לבדוק גם פריימוורקים אחרים אבל לא זאת הנקודה כאן. בואו נסתכל על עוד שני אתרים מעניינים:

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

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

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

ה fix השני מיותר

20/12/2024

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

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

f1ed4259c fix
75e825f62 fix
c388772ec remove unnecessary env group
1cc57ce33 fix sizing of logo
d26a25c47 fix sizing
f510e4f5d update
89007aad9 update
0d9701a14 updates to workflow

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

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

41a1948 fix
0a197d4 fix
e7572cf remove unnecessary env group
daf174b initial commit

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

$ git reset HEAD~3
Unstaged changes after reset:
M       textfile.txt

$ git add .
$ git commit -m 'remove unnecessary env group'

הפרויקט כעת נמצא באותו מצב של קומיט 41a1948 אבל הלוג הוא:

622a0da remove unnecessary env group
daf174b initial commit

ומי שיסתכל בלוג יראה בתוך קומיט 622a0da את כל השינויים של כל שלושת הקומיטים המקוריים.

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

לא צריך לבחור שניים

19/12/2024

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

לפעמים המשולש הזה עוזר לנו להבין את המציאות.

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

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

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

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

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

שלושה טריקים ששיפרו לי משמעותית את ציון הביצועים ב Page Speed

18/12/2024

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

אלה שלושת הטריקים שיישמתי כדי לשפר משמעותית את ציון ה Page Speed שעכשיו עומד על 94 לדף של פוסט מהבלוג:

המשך קריאה

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

17/12/2024

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

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

import { useState, useLayoutEffect, memo, useEffect } from 'react'
import './App.css'

const Text = function Text() {
  return <p>Hello World</p>
}

function App() {
  return (
    <div>
      {new Array(10000).fill(0).map((_, i) => <Text key={i} />)}
    </div>
  )
}

export default App

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

  1. תוספת useEffect ריק בתוך הקומפוננטה מעלה בערך פי 1.5 את זמן ביצוע הסקריפט.

  2. ירידה מ 10,000 קומפוננטות טקסט ל 1,000 קומפוננטות טקסט מתורגמת לזמן ריצת סקריפט פי 5 יותר מהיר.

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

  4. הוספת memo לקומפוננטה מאטה קצת את זמן הריצה.

  5. הוספת state לקומפוננטה כמעט ולא משפיעה על זמן הריצה.

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

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

  2. קומפוננטות דורשות משמעותית יותר משאבים מ DOM Elements. לפעמים שווה לאחד כמה קומפוננטות כדי לשפר ביצועים (בהנחה ששתיהן מושפעות מאותו State).

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

כש Best Practices משתנים

16/12/2024

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

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

  1. מה הפקודה הזאת שראיתי במדריך ואני לא מכיר?

  2. למה היו צריכים אותה?

  3. למה שינו את זה? למה היום כבר לא משתמשים בשיטה הזאת?

  4. (במיוחד עבור שיטות שעדיין עובדות) באיזה מקרים ה Best Practice החדש טוב יותר מהישן? האם יש עדיין סיבות להשתמש בשיטה הישנה?

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

צריך לאהוב

15/12/2024

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

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

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

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

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

  1. אני אוהב לתכנת, אפילו שזה קשה ואני עדיין לא טוב בזה.

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

  3. מעניין מה יקרה אם אכתוב את זה...

  4. מעניין באיזה שפת תכנות השתמשו בשביל לבנות את הכלי הזה...

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

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

שלוש סיבות שגורמות לנו לבנות ארכיטקטורה לא נכונה

14/12/2024

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

המשך קריאה

טיפ פייתון: הרצה משורת הפקודה עם uv

13/12/2024

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

הכלי uv הוא סוג של מנהל גירסאות ומריץ פייתון, הוא יכול להחליף את pyenv וחבריו אבל בשימוש שנראה היום הוא פשוט חוסך לי התקנה של תלויות לתוך תיקיית הפייתון הכללית. נניח שיש לי קוד שנראה כך:

import pandas

mydataset = {
          'cars': ["BMW", "Volvo", "Ford"],
            'passings': [3, 7, 2]
            }

myvar = pandas.DataFrame(mydataset)

print(myvar)

אז הכי הייתי רוצה ש uv יבין לבד משורת ה import שצריך להתקין את pandas, אבל זה עדיין לא קורה. מה שכן אני יכול לעדכן את הקוד ולהוסיף לו כותרת מיוחדת עבור uv באופן הבא:

#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "pandas",
# ]
# ///

import pandas

mydataset = {
          'cars': ["BMW", "Volvo", "Ford"],
            'passings': [3, 7, 2]
            }

myvar = pandas.DataFrame(mydataset)

print(myvar)

ועכשיו אפשר להפעיל את הקסם:

ynonp@Ynons-MacBook-Air ~/tmp $ uv run pandas-demo-uv.py

Reading inline script metadata from `pandas-demo-uv.py`
Installed 6 packages in 38ms
    cars  passings
0    BMW         3
1  Volvo         7
2   Ford         2

באופן אוטומטי uv יצר סביבה וירטואלית בשביל הסקריפט, התקין לתוכה את pandas ולא זיהם לי את ספריית ההתקנה הכללית של פייתון.

את uv אני התקנתי עם homebrew:

$ brew install uv

אבל אפשר גם להתקין אותו עם pip:

pip install uv

ויש לו גם דף תיעוד מרשים בכתובת:

https://docs.astral.sh/uv/