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

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

עבודה יפה בכיוון הלא נכון

22/07/2022

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

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

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

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

המשחק הוא התאמה - לבנות פיתרון יפה שלוקח בחשבון את הבעיה ומביא תוצאות.

לא שווה את המאמץ

21/07/2022

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

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

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

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

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

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

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

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

מה עושים עם התלויות של התלויות ב node

20/07/2022

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

{
  "name": "webapp-demo",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-select": "4.3.1"
  },
  "devDependencies": {
    "@types/react": "^18.0.15",
    "@types/react-dom": "^18.0.6",
    "@vitejs/plugin-react": "^2.0.0",
    "vite": "^3.0.0"
  }
}

אם תנסו לשים אותו בתיקיה ולהריץ npm install זה ייכשל, כי react-select גירסה 4.3.1 צריך את ריאקט 16 או 17, והפרויקט שלי משתמש בגירסה 18 של ריאקט. זאת הודעת השגיאה:

npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: webapp-demo@0.0.0
npm ERR! Found: react@18.2.0
npm ERR! node_modules/react
npm ERR!   react@"^18.2.0" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^16.8.0 || ^17.0.0" from react-select@4.3.1
npm ERR! node_modules/react-select
npm ERR!   react-select@"4.3.1" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR! See /Users/ynonp/.npm/eresolve-report.txt for a full report.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/ynonp/.npm/_logs/2022-07-19T12_28_29_781Z-debug-0.log

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

מה שמביא אותנו לטריק של היום - המילה overrides. עם מפתח overrides אני יכול לשנות את הגירסאות של התלויות של התלויות שלי. בדוגמה שלנו נוסיף את המפתח ל package.json:

{
  "name": "webapp-demo",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-select": "4.3.1"
  },
  "devDependencies": {
    "@types/react": "^18.0.15",
    "@types/react-dom": "^18.0.6",
    "@vitejs/plugin-react": "^2.0.0",
    "vite": "^3.0.0"
  },
  "overrides": {
    "react-select": {
      "react": "18.2.0",
      "react-dom": "18.2.0"
    }
  }
}

והכל מותקן בשלום.

בבדיקה מה הותקן אני יכול לראות:

$ npm ls react

webapp-demo@0.0.0 /Users/ynonp/tmp/blog/webapp-demo
├─┬ react-dom@18.2.0
│ └── react@18.2.0 deduped
├─┬ react-select@4.3.1
│ ├─┬ @emotion/react@11.9.3
│ │ └── react@18.2.0 deduped
│ ├─┬ react-input-autosize@3.0.0
│ │ └── react@18.2.0 deduped invalid: "^16.3.0 || ^17.0.0" from node_modules/react-input-autosize
│ ├─┬ react-transition-group@4.4.2
│ │ └── react@18.2.0 deduped invalid: "^16.3.0 || ^17.0.0" from node_modules/react-input-autosize
│ └── react@18.2.0 deduped
└── react@18.2.0

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

טיפ לימודי: מתחילים לאט

19/07/2022

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

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

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

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

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

מה שמביא אותנו לגישה שניה, לא פחות יעילה, והיא להתחיל לאט:

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

  2. ביום השני רק להתקין את הכלים.

  3. ביום השלישי לבנות פרויקט חדש ולראות שרץ.

  4. ביום הרביעי לשנות משהו קטן בטמפלייט או להוסיף פיצ'ר קטן.

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

חדש באתר: עדכון תכני Redux ו React Router בקורס ריאקט

18/07/2022

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

את הקורס הקלטתי ב 2020 כש Hooks עוד היו דבר חדש, וכשעוד קיוויתי ש create-react-app לא באמת יתפוס.

מאז create-react-app תפס הרבה יותר מדי חזק, והיום אי אפשר לזוז בלעדיו. הוקס השתלטו על העולם והם הדרך הסטנדרטית לבנות ממשקים בריאקט, וריאקט ראוטר כרגיל עשה עוד re-write שלא תומך אחורה.

מה שמביא אותנו לעדכון האחרון-

  1. נוסף שיעור על create-react-app.

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

  3. פרק React Router הוקלט מחדש כדי להתאים ל React Router 6. אני רק מקווה שהחבר'ה שם יתעייפו לפניי מכל ה Breaking Changes שלהם.

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

פחד מכישלון

17/07/2022

זה גדול עליי

אין מצב שאני אצליח להבין את כל החומר

יותר מדי דברים לקרוא

יותר מדי נושאים

בחיים לא אמצא זמן לזה

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

עזוב אני לא בגיל ללמוד עכשיו תחום חדש


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

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

כמה זמן ייקח לך לכתוב טטריס?

16/07/2022

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

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

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

כל ההבדל בינך היום לבין הבן אדם שכותב טטריס בשעה הוא לשבת לכתוב טטריס עשר פעמים. אפשר להתחיל כאן: https://data-flair.training/blogs/python-tetris-game-pygame/

היום למדתי: התקנת הפצה נוספת מאותו סוג ב WSL

15/07/2022

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

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

C:\ wsl --install

ואחרי זה אפשר לכתוב:

C:\ wsl

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

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

C:\ wsl --list --online

ואז אם רוצים להתקין למשל את kali אפשר להפעיל:

C:\ wsl --install -d kali-linux

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

הלינק לגירסה העדכנית של אובונטו הוא: https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64-wsl.rootfs.tar.gz

ואם הורדתם אותו לתיקיית Downloads תצטרכו להפעיל את הפקודה הבאה בשביל להתקין:

C:\ wsl --import ubuntu2 C:\Users\ynonp\Documents\ubuntu2 C:\Users\ynonp\Downloads\ubuntu-22.04-server-cloudimg-amd64-wsl.rootfs.tar.gz

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

אחרי ה import אפשר להיכנס למכונה החדשה עם:

C:\ wsl -d ubuntu2

ושימו לב שמשתמש ברירת המחדל שם הוא root אז תצטרכו ליצור לעצמכם משתמש חדש.

פוטנציאל גנרי

14/07/2022

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

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

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

import urllib.request
import json


zen = urllib.request.urlopen("https://api.github.com/zen").read()
print(zen)

repos = json.loads(urllib.request.urlopen("https://api.github.com/users/ynonp/repos").read())

for repo in repos:
    data = json.loads(urllib.request.urlopen(f"https://api.github.com/repos/ynonp/{repo['name']}").read())
    print(f"Repo {data['name']} has size {data['size']}")

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

import urllib.request
import json

base_url = "https://api.github.com"

def read_string(endpoint):
    return str(urllib.request.urlopen(f"{base_url}/{endpoint}").read())

def read_dict(endpoint):
    return json.loads(urllib.request.urlopen(f"{base_url}/{endpoint}").read())


zen = read_string('zen')
print(zen)

repos = read_dict('users/ynonp/repos')

for repo in repos:
    data = read_dict(f"repos/ynonp/{repo['name']}")
    print(f"Repo {data['name']} has size {data['size']}")

וכאן עוצרים. בנינו קוד עם פוטנציאל גנרי.

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

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

תזרים

13/07/2022

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

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

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

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

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

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

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

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

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