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

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

תשעים ושלוש אחוז נתח שוק

13/08/2023

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

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

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

  3. בסקר של אקליפס מ 2009, חמשים ושמונה אחוז מהמשיבים השתמשו עדיין ב SVN. ב 2011 גיטהאב עקף בפופולריות את SourceForge ושם התחיל אפקט כדור השלג. ב 2015 גיט עדיין עמד על 69% נתח שוק, מספר שהפך ב 2018 ל 87% כשמייקרוסופט רכשה את גיטהאב והכניסה את גיט גם לארגונים גדולים.

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

מבין מה ניסו לעשות שם

12/08/2023

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

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

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

אני מזהה את התבנית הזאת ומאיזה ספר היא הגיעה.

אני רואה את ה Design Pattern שלא ממומש בדיוק לפי הספר אבל גם ברור למה.

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

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

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

משולש האימה

11/08/2023

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

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

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

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

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

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

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

חמישה דברים שהייתי משנה בקלוז'ר

10/08/2023

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

אני כתבתי פוסט מבוא לקלוז'ר בעבר כאן: https://www.tocode.co.il/blog/2019-11-hello-clojure

וגם העברתי יחד עם חבר וובינר על קלוז'ר שמוקלט כאן: https://www.tocode.co.il/past_workshops/95

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

המשך קריאה

יש לי רעיון מבריק!

09/08/2023

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

״מה הבעיה? רק צריך לרשת מ int ולהוסיף ל __add__ פקודת הדפסה. משהו כזה-

class LoggedAddInt(int):
    def __new__(cls, *args, **kwargs):
        args_without_name = {k: v for k, v in kwargs.items() if k != 'name'}
        return super().__new__(cls, *args, **args_without_name)

    def __init__(self, *args, **kwargs):
        self.name = kwargs.get('name', 'New Value')

    def __add__(self, other):
        res = LoggedAddInt(super().__add__(other), name=self.name)
        print(f"{self.name}: {res}")
        return res


counter = LoggedAddInt(0, name="counter")

def do_something():
    global counter
    counter += 1

do_something()
do_something()
do_something()

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

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

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

class NotifyingCounter:
    def __init__(self):
        self.value = 0

    def inc(self):
        self.value += 1
        print(f"counter: {self.value}")

counter = NotifyingCounter()

def do_something():
    counter.inc()

do_something()
do_something()
do_something()

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

אנשים מתים

08/08/2023

הספר The Art Of Deception היה אחד הטובים והמשפיעים שקראתי. בספר קווין מיטניק מתאר דרך דיאלוגים מפורטים ואמינים איך אנחנו תמיד החוליה החלשה באבטחת מידע. הספר לימד אותי חשדנות בריאה ואת הערך של פרטיות.

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

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

טיפ HTML: גם כפתור הוא קלט

07/08/2023

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

אבל מה עושים אם יש מספר כפתורים שכל אחד מהם צריך לגרום לפעולה אחרת?

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

<form method="POST" action="/news">
  <button  name="like" value="like-value" >Like</button>
  <button  name="dislike" value="dislike-value" >Dislike</button>
</form>

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

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

let likes = 0;

router.post('/news', function(req, res, next) {
  console.log(req.body);

  const {like, dislike} = req.body;

  if (like) {
    likes += 1;
  } else if (dislike) {
    likes -= 1;
  }
  res.render('index', { title: 'Express POST /news', likes});
});

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

הפרויקט שלא פורסם

06/08/2023

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

להמשיך לעבוד עליו אחרי שאף אחד לא התלהב? בשביל מה? לא עדיף להתרכז בדברים שיש להם ערך?

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

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

בואו נפתור יחד את פרויקט אוילר תרגיל 11 בשפת Python

05/08/2023

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

https://projecteuler.net/archives

בתרגיל 11 יש מטריצה של מספרים:

08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08
49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00
81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65
52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91
22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80
24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50
32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70
67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21
24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72
21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95
78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92
16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57
86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58
19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40
04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66
88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69
04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36
20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16
20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54
01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48

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

המשך קריאה

חדש באתר: מיני קורס Type Hints בפייתון

04/08/2023

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

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

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

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

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

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

  3. איך (ולמה) לעבוד עם TypeVar.

  4. איך להגדיר פרוטוקול ומתי להשתמש בו, ואיזה פרוטוקולים כבר מוגדרים בשפה.

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

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

מנויים לאתר יכולים כבר לצפות בתוכן בקישור: https://www.tocode.co.il/boosters/13.

ואם אתם עדיין לא מנויים היום הוא הזדמנות מצוינת להירשם.

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