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

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

היום למדתי: התקנת הפצה נוספת מאותו סוג ב 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 המסובך לאחרי שהמוצר באוויר.

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

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

כמה זמן לוקח לכתוב דיאלוג ב CSS?

12/07/2022

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

  • כמה זמן לוקח לכתוב דיאלוג מודאלי בדפדפן? שניה וחצי, רק שורה ב jQuery UI

  • כמה זמן לוקח לכתוב דיאלוג מודאלי בדפדפן? שניה וחצי, רק שורה ב Bootstrap

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

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

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

רק העתקתי

11/07/2022

"אני בסך הכל העתקתי את הקוד ממקום אחר במערכת"

"הבאג הזה היה שם גם קודם"

"זה באג בפריימוורק לא קשור אלינו"

"אף אחד לא ישים לב וצריך להתקדם עם הגירסה"

"מישהו אי פעם התלונן על זה?"

"זה בכלל פיצ'ר"

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

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

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

פרויקט צד

10/07/2022

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

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

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

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

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

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 חייבת להיות קיימת על המחשב המארח בתיקיית הפיתוח (יכולה להיות ריקה), אחרת המיפוי נכשל והקונטיינר לא יעלה.