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

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

להחזיר או לזרוק

27/11/2024

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

>>> d = {"a": 10, "b": 20}
>>> d.get("a")
10
>>> d.get("d")

או עם אופרטור סוגריים מרובעים שזורק שגיאה אם הערך לא קיים:

>>> d['a']
10
>>> d['d']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'd'

בסקאלה הפונקציה get מחזירה ערך ממפה ואם הוא לא קיים מחזירה None:

scala> Map("a" -> 10, "b" -> 20).get("a")
val res1: Option[Int] = Some(10)

scala> Map("a" -> 10, "b" -> 20).get("d")
val res3: Option[Int] = None

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

scala> Map("a" -> 10, "b" -> 20)("a")
val res2: Int = 10

scala> Map("a" -> 10, "b" -> 20)("d")
java.util.NoSuchElementException: key not found: d
  at scala.collection.immutable.Map$Map2.apply(Map.scala:316)
  ... 30 elided

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

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

ומה אתכם? איזה משתי השיטות אתם מעדיפים? ומה לגבי ערבוב השיטות באותו פרויקט?

על הערך של "להיות בעשייה"

26/11/2024

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

  1. "לעולם לא אהיה מספיק טוב בזה. אני רק מבזבז את הזמן עכשיו".

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

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

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

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

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

אבל ביקשתי לשים את זה באמצע

25/11/2024

נתבונן על הגדרת ה CSS הבאה:

body, html {
  height: 100vh;
  padding: 0;
  margin: 0;
}

main {
  display: flex;
  place-items: center;
  background: #a0a0a0;
}

וה HTML שבתוך ה body כולל בסך הכל:

<main>hello world</main>

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

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

  2. הפקודה place-items: center באמת קובעת ל main למקם את הילדים שלו באמצע שלו. אבל שימו לב שכל גובהו הוא שורה אחת ולכן השורה הבודדת תופסת את כל הגובה.

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

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

  1. נקבע את הגובה של main למספר קבוע או לגובה של המיכל שלו, במקום לגובה של התוכן שלו, על ידי הגדרת height: 100%.

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

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

סך הכל הדוגמה עם היישור בקודפן:

ובקישור: https://codepen.io/ynonp/pen/WbeeQqm

מורים בעידן ה Chat GPT

24/11/2024

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

אז מה נשאר למורים לעשות? הנה שלוש משימות חשובות:

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

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

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

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

פרויקט דוגמה: next עם בסיס נתונים drizzle

23/11/2024

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

המשך קריאה

שימו לב - ריאקט 19 ו next

22/11/2024

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

למה זה מעייף? הנה התקנה חדשה של next ו drizzle:

$ npx create-next-app@latest my-next-app
$ cd my-next-app
$ npm i drizzle-kit drizzle-orm @libsql/client dotenv

npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error
npm error While resolving: next-and-sql-demo@0.1.0
npm error Found: react@19.0.0-rc-66855b96-20241106
npm error node_modules/react
npm error   react@"19.0.0-rc-66855b96-20241106" from the root project
npm error   peer react@"*" from @op-engineering/op-sqlite@10.1.0
npm error   node_modules/@op-engineering/op-sqlite
npm error     peerOptional @op-engineering/op-sqlite@">=2" from drizzle-orm@0.36.3
npm error     node_modules/drizzle-orm
npm error       drizzle-orm@"*" from the root project
npm error   4 more (expo-sqlite, expo, @expo/dom-webview, react-native-webview)
npm error
npm error Could not resolve dependency:
npm error peerOptional react@">=18" from drizzle-orm@0.36.3
npm error node_modules/drizzle-orm
npm error   drizzle-orm@"*" from the root project

וזה של react-select:

$ npm install react-select

npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error
npm error While resolving: next-and-sql-demo@0.1.0
npm error Found: react@19.0.0-rc-66855b96-20241106
npm error node_modules/react
npm error   react@"19.0.0-rc-66855b96-20241106" from the root project
npm error
npm error Could not resolve dependency:
npm error peer react@"^16.8.0 || ^17.0.0 || ^18.0.0" from react-select@5.8.3
npm error node_modules/react-select
npm error   react-select@"*" from the root project

ויש עוד המון.

עכשיו אני יודע איך לשנמך את הריאקט בפרויקט נקסט החדש שלי (כל עוד הוא משתמש ב Pages Router, כי App Router כן דורש את ריאקט 19). אני גם יודע איך להישאר בגירסה 14 של נקסט עוד קצת, למרות ש 15 כולל שיפורים ותיקוני באגים והייתי מעדיף לקבל אותם לפרויקט. במקרה הגרוע אני גם יודע להכריח את npm להתקין את התלויות או לעשות fork לתלויות ולבנות גירסאות שלהן שמתאימות לריאקט 19. וכן אפשר גם לחכות עד שיצא ריאקט 19.

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

מציפיות לתוכנית עבודה

21/11/2024

יש שתי בעיות עם ציפיות גבוהות מדי:

  1. כשזה לא מצליח אנחנו נוטים להתאכזב ולהתייאש.

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

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

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

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

מה באמת הבעיה בדוגמת ריאקט הזו

20/11/2024

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

https://matanbobi.dev/posts/stop-passing-setter-functions-to-components

הקוד שהוא הדביק נראה ככה:

// Form.jsx 
function Form() { 
    const [formData, setFormData] = useState({ name: '' }); 
    return (
        <div>
            <h1>Form</h1> 
            {/* Pass the setter function down to ChildComponent */}
            <Input name={formData.name} setFormData={setFormData} />
            <button onClick={() => console.log(formData)}>Submit</button> 
        </div> 
    ); 
};

// Input.jsx
function Input({ name, setFormData }) { 
    const handleInputChange = (event) => { 
        // Directly using the setFormData setter function from the parent
        setFormData((prevData) => ({ ...prevData, name: event.target.value })); 
    }; 

    return ( 
        <div>
            <label> 
                Name: 
                <input type="text" value={name} onChange={handleInputChange} /> 
            </label>
        </div> 
    ); 
};

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

setFormData((prevData) => ({ ...prevData, name: event.target.value })); 

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

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

// Form.jsx 
function Form() { 
    const [name, setName] = useState(''); 
    const [favoriteColor, setFavoriteColor] = useState('#000000);

    return (
        <div>
            <h1>Form</h1> 
            <Input label="Name" value={name} setter={setName} />
            <Input label="Favorite Color" value={favoriteColor} setter={setFavoriteColor} />
        </div> 
    ); 
};

// Input.jsx
function Input({ label, value, setter }) { 
    const handleInputChange = (event) => { 
        setter(event.target.value);
    }; 

    return ( 
        <div>
            <label> 
                {label}: 
                <input type="text" value={value} onChange={handleInputChange} /> 
            </label>
        </div> 
    ); 
};

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

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

אנחנו בונים

19/11/2024

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

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

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

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

כמה באמת עולה כרטיס לוטו?

18/11/2024

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

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

  1. "אם לא תנסה איך תזכה"

  2. "זה רק 20 ש"ח, חבל לא לנסות"

  3. "ממילא אין לי משהו אחר לעשות עם הכסף"

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

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

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

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