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

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

חמישה בוטים לטלגרם שתוכלו לכתוב כפרויקט צד

18/06/2023

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

המשך קריאה

בול בפוני

17/06/2023

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

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

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

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

שילוב React Router Data API עם RTK Query

16/06/2023

אחד השילובים האהובים עליי בתקופה האחרונה הוא החיבור בין Data API של React Router ל RTK Query. בקצרה, ה Data API אומר שאנחנו יכולים להוציא את כל הלוגיקה של "מתי" מידע נטען מהשרת ל Router במקום שיישמר בקומפוננטות, ו RTK Query יודע למדל טוב את הלוגיקה של "איך" מידע נטען מהשרת. יחד הם שילוב מנצח כפי שנראה בפוסט הבא.

המשך קריאה

לחפש את ה"לא אפשרי"

15/06/2023

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

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

לדוגמה בעבודה עם Immutable Data - בגלל שאי אפשר לשנות את המידע עצמו קל יותר להשוות בין דברים ולזהות מהר אם משהו השתנה (כי מספיק להשוות מצביעים).

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

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

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

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

איך להתמודד עם FormData מתוך קוד Express

14/06/2023

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

המשך קריאה

צרות של ריאקטיביים

13/06/2023

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

עכשיו הבעיה שלהם הפכה להיות איך לזהות ש r/atom כזה השתנה, והתשובה היא ריקטיביות. בדומה ל Vue ועוד יותר ממנו ל Solid, גם ריאייג'נט מסתכל בזמן חישוב הקומפוננטה לאיזה אטומים ניגשו, ומחבר אותם לקומפוננטה כך ששינוי בערך האטום יגרום לחישוב מחדש של הקומפוננטה.

לכן הקוד הבא ל Counter עובד:

(defn counter []
  ;; run on init, skip on re-calculations
  (r/with-let [value (r/atom 0)]
    [:div
     {:style {:margin "10px"}}
     [:p (str "Value " @value)]
     [:button {:on-click #(swap! value inc)} "+"]
     [:button {:on-click #(swap! value dec)} "-"]]))

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

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

(defn textboxes []
  (r/with-let [value (r/atom "hello")]
    [:div
     (for [i (range 5)]
       [:input {:value @value
                :on-change (fn [ev] (reset! value (.. ev -target -value)))}])]))

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

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

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

(defn textboxes []
  (r/with-let [value (r/atom "hello")]
    [:div
     (doall
      (for [i (range 5)]
        [:input {:value @value
                 :on-change (fn [ev] (reset! value (.. ev -target -value)))}]))]))

הזמן הנכון להינעל על פיתרון

12/06/2023

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

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

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

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

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

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

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

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

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

זה מעולה, אבל...

11/06/2023

זה מעולה. שווה גם לנסות למצוא אלגוריתם יותר יעיל.

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

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

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

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

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

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

לא נשמע כמו עוד משתנה State

10/06/2023

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

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

המשך קריאה

רק לסובב את הפדאלים

09/06/2023

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

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

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

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

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

המטרה היא לא למצוא את התשובה הכי מהר, אלא למצוא את התשובה הנכונה.