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

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

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

05/08/2020

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

המשך קריאה

קורסים שמפחידים אותי

04/08/2020

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

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

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

(מה זה תבניות לא נכונות? לדוגמא בשפה כמו C זה יהיה כמו ללמד את malloc בלי ללמד את free. ב JavaScript זה יהיה ללמד את הפונקציה document.write, ועוד דברים רבים בסגנון).

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

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

איך לצמצם Boilerplate בקוד Redux באמצעות ES6 Proxy

02/08/2020

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

המשך קריאה

הנתיב השמח

01/08/2020

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

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

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

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

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

לרמות את המערכת

31/07/2020

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

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

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

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

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

למה צריך ואיך לנהל State גלובאלי של יישום ריאקט עם MobX

30/07/2020

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

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

המשך קריאה

הבדיקות ממש הצילו אותי הפעם

29/07/2020

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

״הבדיקות ממש הצילו אותי הפעם״

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

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

דוגמת Refactoring ב React כדי לקבל Markup נקי יותר

28/07/2020

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

במילים אחרות אם כתבתי 2 בתיבת השעות אוטומטית יופיע 120 בתיבת הדקות ו 7200 בתיבת השניות. ואם כתבתי 60 בתיבת הדקות אוטומטית יופיע 1 בתיבת השעות ו 3600 בתיבת השניות. אני משנה טקסט בתיבה אחת והשתיים האחרות צריכות להתעדכן בהתאמה.

הרבה מאוד פעמים אנשים יפתרו את התרגיל עם קוד שנראה בערך כך:

function TimeConverter(props) {
    const [seconds, setSeconds] = useState(0);

    return (
        <div>
            <label>
                Seconds:
                <input
                    type="number"
                    value={seconds}
                    onChange={e => setSeconds(e.target.value)}
                />
            </label>
            <label>
                Minutes:
                <input
                    type="number"
                    value={seconds / 60}
                    onChange={e => setSeconds(e.target.value * 60)}
                />
            </label>
            <label>
                Hours:
                <input
                    type="number"
                    value={seconds / 3600}
                    onChange={e => setSeconds(e.target.value * 3600)}
                />
            </label>
        </div>
    );
}

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

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

function TimeConverter(props) {
    const time = useTime(0);

    return (
        <div>
            <SecondsPanel time={...time} />
            <MinutesPanel time={...time} />
            <HoursPanel time={...time} />
        </div>
    );
}

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

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

function useTime(initialValue) {
    const [value, setter] = useState(initialValue);
    return {
        time: value,
        setTime: setter,
    };
}

בשלב ראשון אני יכול לכתוב את שלושת הקומפוננטות עם אותו קוד שהיה לי ב JSX הראשוני:

function Seconds(props) {
    const { time, setTime } = props;

    return (
                <label>
                    Seconds:
                    <input
                        type="number"
                        value={time}
                        onChange={e => setTime(e.target.value)}
                    />
                </label>
    );
}

function Minutes(props) {
    const { time, setTime } = props;
    return (
            <label>
                Minutes:
                <input
                    type="number"
                    value={time / 60}
                    onChange={e => setTime(e.target.value * 60)}
                />
            </label>
    );
}

function Hours(props) {
    const { time, setTime } = props;
    return (
            <label>
                Hours:
                <input
                    type="number"
                    value={time / 3600}
                    onChange={e => setTime(e.target.value * 3600)}
                />
            </label>
    );
}

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

function timeUnit(name, factor) {
  const TimeUnit = (props) => {
    const { time, setTime } = props;
    return (
      <label>
        {name}
        <input
          type="number"
          value={time / factor}
          onChange={e => setTime(e.target.value * factor)}
          />
      </label>
    )
  }
  TimeUnit.name = name;
  return TimeUnit;
}

ובאמצעותה ליצור את הקומפוננטות:

const Seconds = timeUnit('Seconds', 1);
const Minutes = timeUnit('Minutes', 60);
const Hours = timeUnit('Hours', 3600);

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

function timeUnit(name, factor) {
  const TimeUnit = (props) => {
    const { time, setTime } = props;
    return (
      <label>
        {name}
        <input
          type="number"
          value={time / factor}
          onChange={e => setTime(e.target.value * factor)}
          />
      </label>
    )
  }
  TimeUnit.name = name;
  return TimeUnit;
}

function useTime(initialValue=0) {
    const [value, setter] = React.useState(0);
    return { time: value, setTime: setter };
}

const Seconds = timeUnit('Seconds', 1);
const Minutes = timeUnit('Minutes', 60);
const Hours = timeUnit('Hours', 3600);

function TimeConverter(props) {
  const time = useTime();

  return (
    <div>
      <Seconds {...time} />
      <Minutes {...time} />
      <Hours {...time} />
    </div>
  );
}

ReactDOM.render(<App />, document.querySelector('main'));

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

כמה זה עולה לנו?

27/07/2020

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

האם וובסטורם שווה יותר מ Sublime Text כי הוא עולה 59$ בשנה וסאבליים 80$ לשלוש שנים? האם בייסקמפ שווה יותר או פחות מג'ירה? וזה עוד החלק הקל כי המחיר הוא בדולרים.

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

שתי דוגמאות מהחיים-

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

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

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

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