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

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

איזה סוג של אבוד

09/07/2022

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

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

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

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

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

געגועים ל Higher Order Components

08/07/2022

לפני Hooks היינו משתמשים בריאקט בתבנית קצת מסורבלת שנקראה Higher Order Components כדי לשתף Stateful Logic בין כמה קומפוננטות, מה שהיום אנחנו עושים בקלות עם Custom Hooks. אבל הרעיון מאחורי Higher Order Components יכול לעזור גם בכתיבת קומפוננטות מודרניות ולאפשר שיתוף קוד טוב ופשוט באפליקציה. ככה זה עובד.

המשך קריאה

ללמוד להנות מתכנות

07/07/2022

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

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

אבל יש גם דרך אחרת.

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

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

אז מה כן אפשר לעשות?

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

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

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

טיפ דוקר: מה עושים עם node_modules

06/07/2022

בפיתוח יישומי node.js בתוך דוקר אני בדרך כלל אוהב להריץ npm install בתוך הקונטיינר בתור פקודה ראשונה כשהוא עולה, לפני שמפעיל את היישום האמיתי שלי, וככה אני יודע שתמיד כל החבילות יהיו במקום. ייתרון נוסף של הגישה הזאת הוא שאפשר להשתמש ב image שכבר קיים בדוקרהאב ב docker-compose.yml וזה חוסך לנו את שלב בניית האימג'.

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

במצב כזה האינטואיציה הראשונה היא להריץ npm install מתוך ה Dockerfile, למשל משהו כזה:

FROM node:18
WORKDIR /app

COPY package*.json .
RUN npm install

CMD ["node", "myapp.js"]

ואז אני משתמש בקובץ docker-compose.yml כזה כדי למפות את התיקיה הנוכחית (סביבת הפיתוח) פנימה לקונטיינר:

version: "3.9"
services:
  app:
    build: .
    command: sleep infinity
    volumes:
      - .:/app

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

זה קורה בגלל שמיפוי ה Volume של תיקיית הפיתוח מסתיר את קבצי ה node_modules שהתקנתי בעת בניית האימג'.

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

ב node לצערי זה לא אפשרי ו node_modules ממש חייב להיות בתוך תיקיית קוד המקור שלכם, ולכן הפיתרון דורש קצת יותר יצירתיות: אנחנו מגדירים Volume בתוך ה Dockerfile שיכיל את המודולים המותקנים, ואז בהרצה ממפים את אותו volume על גבי המיפוי של תיקיית המקור - כך יוצא שה volume החדש של ה node_modules מסתיר את המיפוי של node_modules מתיקיית המקור, שהוא בעצמו הסתיר את תיקיית node_modules של האימג' (שעכשיו ריקה כי ההתקנה היתה על ה Volume).

קוד? ברור. זה ה Dockerfile - התוספת היחידה היא פקודת volume:

FROM node:18
WORKDIR /app

COPY package*.json .

VOLUME /app/node_modules
RUN npm install

CMD ["node", "myapp.js"]

כשמפעילים את האימג', פקודת run תעתיק את התוכן של node_modules שנמצא באימג' לתוך ה Volume החדש, והוא ימופה לתוך /app/node_modules במקום התיקיה מהמחשב המארח.

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

למה סטאק אוברפלו צריכים בדיקות יחידה?

05/07/2022

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

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

  2. הם רוצים להיות מסוגלים לעשות Refactoring בקלות בלי לשבור פונקציונאליות ללקוחות קיימים.

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

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

  5. בדיקות טובות מתפקדות כמו תיעוד לקוד. אפשר לקרוא את הבדיקות ולהבין מה כל חלק עושה.

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

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

מבוא ל React Router גירסה 6

04/07/2022

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

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

המשך קריאה

הזמנה לוובינר - חידושים בריאקט 18

03/07/2022

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

  1. נראה מהו useDeferredValue, איזה בעיה הוא בא לפתור ואיזה בעיות הוא יוצר.

  2. ממנו נגיע לדבר על Concurrent Rendering, שזה הפיצ'ר שמאפשר את useDeferredValue ונעבור על הדוגמה של useTransition כדי לראות עוד ייתרון של הרינדור המקבילי.

  3. נדבר על הקושי בעבודה עם רינדור מקבילי, ונראה את הדוגמה של Render Tearing.

  4. נדבר על ההתנהגות החדשה של Strict Mode שעלולה לבלבל במצב פיתוח, ועל השינוי בהתנהגות של useEffect שוב בעקבות ה Concurrent Rendering.

הוובינר יעבור בזום ביום חמישי בעשר בבוקר. הקישור לפרטים והרשמה הוא: https://www.tocode.co.il/workshops/117

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

נתראה בחמישי, ינון

ריאקט 18 סוף סוף מציע פיתרון לבעיית ה label-ים

02/07/2022

עד שהגיע ריאקט או בכלל הרעיון של קומפוננטות, מתכנתים ומתכנתות כתבו קבצי HTML גדולים ובתוכם היו label-ים ו input-ים המתואמים ביניהם באמצעות id:

<label for="name">User Name</label>
<input type="text" id="name" />

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

<label>
User Name
<input type="text" />
</label>

ועד דברים יותר מתוחכמים כמו המצאת מזהים:

function MyForm() {
    const id = Math.random().toString(16);
    return (
        <>
            <label htmlFor={id}>User Name</label>
            <input type="text" id={id} />
        </>
    );
}

הבעיה בפיתרון הראשון היא שהוא מכריח markup מסוים. כל עוד זה עובד לכם הכל טוב, אבל לפעמים באמת ה label וה input לא צמודים אחד לשני. הבעיה בפיתרון השני היא שכנראה לא נקבל את אותו id ב Server Side Rendering, מה שייצור אי תאימות כשריאקט יריץ את כל ה render-ים בדפדפן ויקבל HTML עם מזהים שונים.

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

import { useId } from 'react';

function MyForm() {
    const id = useId();
    return (
        <>
            <label htmlFor={id}>User Name</label>
            <input type="text" id={id} />
        </>
    );
}

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

מודל מנטאלי

01/07/2022

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

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

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

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

היום למדתי: הקשר בין globals לניקוי ה DOM ב vitest

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

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: "jsdom",
  },
})

אז בכל קבצי הבדיקות שלכם תוכלו להסתמך על זה שיש describe, it ו expect ואולי עוד כמה משתנים גלובאליים. ברירת המחדל היא false ואז צריך לייבא הכל לבד עם שורה כזאת בתחילת קובץ בדיקות:

import { describe, it, expect } from 'vitest';

עד לפה אין סיבה להתרגש, אבל מסתבר שלהזרקת ה globals יש עוד אפקט והוא הרבה יותר מורגש: בעת בדיקת קומפוננטות ריאקט עם react-testing-library, הספריה תנקה את ה DOM בין בדיקה לבדיקה אם ה globals מוזרקים, אבל לא תנקה בלעדיהם.

במילים אחרות בקובץ בדיקות כזה:

import { describe, it, expect } from 'vitest';
import { screen, render } from '@testing-library/react';
import App from './App';

describe('App Changes Colors', () => {
  it('shows a button', () => {
    render(<App />);
    const btn = screen.getByRole('button', { name: '0' });
    expect(btn).toBeTruthy();
  });
  it('shows a button', () => {
    render(<App />);
    const btn = screen.getByRole('button', { name: '0' });
    expect(btn).toBeTruthy();
  });
});

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

הפונקציה הרלוונטית מ testing-library שמטפלת בסיפור המחיקה נקראת cleanup וזה מה שמוסבר גם בתיעוד שלה:

    Please note that this is done automatically if the testing framework you're using supports the afterEach global and it is injected to your testing environment (like mocha, Jest, and Jasmine). If not, you will need to do manual cleanups after each test.

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