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

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

מעגלים

18/02/2020

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

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

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

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

חידת שיפור ביצועים ב React

17/02/2020

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

ועם הרמז הזה אני שולח אתכם לקוד בקישור: https://codesandbox.io/s/sparkling-framework-zc8c1.

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

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

בהצלחה. ינון

יותר מסובך ממה שחשבתי שיהיה

15/02/2020

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

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

״אני צריכה מסגרת מוגדרת בשביל להתקדם וללמוד״

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

״אני פוחדת לעבוד שבועיים רק בשביל לגלות בסוף שהלכתי בדרך הלא נכונה״

״אני פוחד להיתקע ולגלות שהפרויקט הזה גדול עליי״

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

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

למה ואיך להשתמש ב Generics ב TypeScript

14/02/2020

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

עד שמגיעים לכתוב Custom Hooks.

ניקח לדוגמא את הקוד הבא שמגדיר Custom Hook שפונה לשרת להביא מידע בתור JSON:

function useRemoteData(endpoint: string, id: string) {
    const [data, setData] = useState<any|null>(null);

    useEffect(function() {
        setData(null);
        const CancelToken = axios.CancelToken;
        const source = CancelToken.source();

        const req = axios.get(`https://swapi.co/api/${endpoint}/${id}/`, {
            cancelToken: source.token,
        });
        req.then(function(response) {
            // when we get response
            setData(response.data);
        });

        return function cancel() {
            source.cancel();
        }
        // code continues here
    }, [id]);

    return data;
}

הקוד עובד אבל שימו לב לשורה שמגדירה את סוג המשתנה data:

    const [data, setData] = useState<any|null>(null);

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

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

function FilmInfo(props: { id: string }) {
    const { id } = props;
    // Get character data ???
    const data = useRemoteData('films', id);

    if (data === null) {
        return <p>Loading, please wait...</p>
    }

    return (
        <div>
            <p>title: {data.title}</p>
            <p>release_date: {data.release_date}</p>
            <hr />
        </div>
    )
}

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

    const data = (useRemoteData('films', id) as IDataFilm);

ולהוסיף באיזשהו מקום בקובץ את הגדרת הממשק IDataFilm. אבל זה לא באמת פותר לנו את הבעיה: לא כולם יודעים או זוכרים שכדאי להשתמש ב as כל פעם לפני שמפעילים פונקציה ומהר מאוד נתחיל לראות מתכנתים שמוותרים על זה. דרך קצת יותר ברורה היא להשתמש ב Generics. המילה Generics בסך הכל אומרת שהקוד שקורא ל Hook חייב להעביר גם את סוג המידע שהוא מצפה לקבל, ואז ה Custom Hook שלנו יראה כך:

function useRemoteData<T>(endpoint: string, id: string) {
    const [data, setData] = useState<T|null>(null);

    useEffect(function() {
        setData(null);
        const CancelToken = axios.CancelToken;
        const source = CancelToken.source();

        const req = axios.get(`https://swapi.co/api/${endpoint}/${id}/`, {
            cancelToken: source.token,
        });
        req.then(function(response) {
            // when we get response
            setData(response.data);
        });

        return function cancel() {
            source.cancel();
        }
        // code continues here
    }, [id]);

    return data;
}

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

function FilmInfo(props: { id: string }) {
    const { id } = props;
    // Get character data ???
    const data = useRemoteData<IDataFilm>('films', id);

    if (data === null) {
        return <p>Loading, please wait...</p>
    }

    return (
        <div>
            <p>title: {data.title}</p>
            <p>release_date: {data.release_date}</p>
            <hr />
        </div>
    )
}

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

טיפ גיט: קומיט זה לא הסוף

13/02/2020

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

כמעט כל המתכנתים שראיתי שעובדים בגיט מרגישים שאחרי commit חייבים לעשות push.

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

שימו לב ל-4 קומיטים לדוגמא:

9a550c6 (HEAD -> master) actually this looks better in a class
e837614 changed some texts
3aa43e8 moved code to function
3785d2d intiial commit

ה diff בין הקומיט הישן ביותר לחדש ביותר נראה כך:

diff --git a/a.rb b/a.rb
index 15aaec7..de05881 100644
--- a/a.rb
+++ b/a.rb
@@ -1 +1,8 @@
-puts "hello world"
+class Greeter
+  def hi
+    puts "Hello World"
+  end
+end
+
+g = Greeter.new
+g.hi

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

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

$ git reset --soft 3785d2d
$ git add .
$ git commit -m 'Changed code to use object oriented syntax'

וקיבלתי לוג הרבה יותר נקי:

6a4e1ad (HEAD -> master) Changed code to use object oriented syntax
3785d2d intiial commit

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

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

נ.ב. רוצים ללמוד יותר לעומק איך לעבוד ב git ולשחק עם קומיטים כאילו היו כדורי ג'אגלינג? יש לי קורס וידאו שתפור בדיוק עליכם. מתחילים כאן: https://www.tocode.co.il/bundles/git/

שלוש סיבות לשלב TypeScript בפרויקט ריאקט הבא שלכם

12/02/2020

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

המשך קריאה

שלבים לפיתרון בעיה

11/02/2020

  1. אני לא מבין מה רוצים ממני

  2. אין לי מושג איך לגשת לזה

  3. יש לי רעיון אבל אין לי מושג איך ליישם אותו

  4. התחלתי ליישם את הרעיון אבל זה לא עבד - לא ברור לי למה

  5. הבנתי למה הרעיון שלי לא עבד. עכשיו שוב אין לי שום רעיון.

  6. ממשיכים את 2-5 בלולאה, לפעמים כמה עשרות פעמים. כל פעם לומדים עוד משהו על הבעיה ועל עוד דרך שלא עובדת.

  7. הצלחתי ליישם פיתרון שעובד!

  8. אה, אבל בעצם יש את מקרה הקצה הזה שלא חשבתי עליו.

  9. יש לי רעיון איך לתקן את מקרה הקצה הבעייתי.

  10. ממשיכים את 8-9 בלולאה, לפעמים כמה עשרות פעמים.

  11. יש לי קוד שעובד!

  12. מה המשימה הבאה?

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

בשביל מה את בונה את זה

10/02/2020

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

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

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

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

ארבעה רעיונות פשוטים לפרויקטים ב Arduino

09/02/2020

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

המשך קריאה