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

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

קודם כל אמון

12/01/2021

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

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

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

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

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

גם תהליכי עבודה זה תשתית

11/01/2021

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

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

  1. איך מדבגים את זה?

  2. מה מינימום בדיקות שצריך לכתוב בעבודה על פיצ'ר?

  3. למה אנחנו עושים Mock בבדיקה? ולמה לא?

  4. מה שומרים ב git?

  5. מה הפורמט של הודעת קומיט?

  6. איך מעלים גירסה?

  7. איך פותחים פרויקט חדש? מה צריך להתקין כשמקבלים מחשב חדש?

  8. מה עושים כשכמה אנשים צריכים לעבוד על פיצ'רים שונים בפרויקט?

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

איך להריץ סקריפט כל פעם שהלפטופ מתחבר או מתנתק מהחשמל ב Ubuntu

10/01/2021

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

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

$ sudo apt-get install pm-utils

חבילת pm-utils כוללת שתי פקודות מעניינות. הראשונה מכניסה אותנו למצב חיסכון בחשמל:

sudo pm-powersave true

והשניה חוזרת למצב עבודה רגיל:

sudo pm-powersave false

פקודות אלה מחפשות סקריפטים בתוך תיקיית /etc/pm/power.d ויריצו את כל הסקריפטים שם לפי סדר, כשכל סקריפט מופעל עם הפרמטר true או false (הפרמטר true מציין כניסה למצב חיסכון בחשמל, false מציין יציאה ממצב זה).

עד לפה הכל טוב ויפה אבל רובנו לא הולכים לזכור כל פעם שאנחנו מנתקים את המחשב מהחשמל גם להריץ את הסקריפט של pm-powersave ולכן צריך עוד שינוי קטן כדי שמנגנון זה ירוץ אוטומטית בעת ניתוק או חיבור המחשב לחשמל. אחרי חיפוש קצר ברשת מצאתי שדרך קלה לעשות את זה היא להוסיף udev rule. בתור מנהל מערכת ניצור את הקובץ /etc/udev/rules.d/99-powersave.rules ובתוכו נכתוב:

SUBSYSTEM=="power_supply", ATTR{online}=="0", RUN+="/usr/sbin/pm-powersave true"
SUBSYSTEM=="power_supply", ATTR{online}=="1", RUN+="/usr/sbin/pm-powersave false"

לאחר מכן הפעילו את הפקודה הבאה כדי לרענן את כללי ה udev:

udevadm control --reload-rules && udevadm trigger

וצרו את הסקריפטים שאתם צריכים בתיקיית /etc/pm/power.d. כל סקריפט שתיצרו שם יופעל אוטומטית כשהמערכת נכנסת למצב חיסכון בחשמל או יוצאת ממנו. בדף התיעוד על ניהול צריכת חשמל תוכלו למצוא דוגמאות לסקריפטים כאלה וכלים שיעזרו לכם לצמצם את צריכת החשמל של המחשב כשהוא לא מחובר לקיר.

ואם זה מפסיק להיות כיף?

09/01/2021

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

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

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

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

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

טעות בתהליכי עבודה

08/01/2021

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

טעויות אנוש קורות: כשמתכנת עושה Deploy ל Production במקום לסביבת הטסט זו טעות אנוש; כשאתה מוחק בטעות את הקובץ package.json במקום את package-lock.json זו טעות אנוש; אפילו כשאתה מריץ את הטסטים בטעות על מכונת הפרודקשן ומוחק את כל בסיס הנתונים זו טעות אנוש. טעות אנוש קורית לבן אדם שיודע מה צריך לעשות ובטעות עשה את הדבר הלא נכון.

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

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

למה זה לא מתרסק (או: בעיות שלא רואים)

07/01/2021

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

for _ in range(100_000):
    fout = open('output.txt', 'w')
    fout.write("hahaha\n")

מאחר ומספר ה File Handlers הפתוחים בלינוקס שלי מוגבל ל 1024, ציפיתי להתרסקות מהירה. היא לא הגיעה.

מחשבים רוצים לעזור (חוץ מ C++, אבל היא באמת מסיפור אחר), ובמקרה של פייתון הניסיון הזה מתבטא בזה שפייתון שמה לב שכל איטרציה של הלולאה מאבדת את הגישה ל File Handle הקודם שנוצר ולכן מרשה לעצמה לסגור את הקובץ, גם בלי close וגם בלי with.

האם זה אומר שכדאי לי להתרגל לכתוב כך קוד? אני לא בטוח. יש תוכניות ששומרות הרבה אוביקטים בזיכרון ושם יכול להיות שפייתון לא ימחק את ה File Handles שיצרנו כי התוכנית עדיין שומרת מצביע אליהם. אולי יותר מטרידה האפשרות שקוד שמופיע במערכת ישתכפל (כי מתכנתים רגילים לעשות copy-paste ממקומות אחרים בקוד) ויגיע למקומות בהם הוא כן יוביל לזליגת קבצים פתוחים.

לקריאה נוספת בנושא שווה לקרוא את הפוסט הבא של ראובן לרנר שגם עשה ניסויים עם מימושים נוספים של פייתון (pypy ו jython): https://lerner.co.il/2015/01/18/dont-use-python-close-files-answer-depends/.

איך נוצר חוב טכני

06/01/2021

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

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

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

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

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

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

  5. כשאין לנו תשתית בדיקות או שאנחנו מוכנים להשלים עם בדיקות שנכשלות.

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

אם הייתי מתחיל היום פרויקט SaaS חדש

05/01/2021

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

חוץ מהבעיה של Scope גדול מדי לפרויקט עצמו קשה לקרוא את הפוסט בלי לחשוב על כמות העבודה שסבסטיאן השקיע בשימוש בטכנולוגיה מודרנית. בין השאר הוא מספר על שלושה חודשים של השקעה בפיתוח Build System ו CI, ובהמשך גם על השימוש ב Kubernetes ואפילו השקעה של 3,000 אירו ב UX/UI. כל זה לפני שיש לקוח משלם ראשון.

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

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

  1. פריימוורק פיתוח ווב כמה שיותר מלא - לדוגמה Rails או Django.

  2. בלי פריימוורק צד לקוח, מקסימום jQuery.

  3. מבנה Monolith, בלי Micro Services.

  4. קונה Theme מוכן מהרשת, מוכן לעשות קצת התאמות.

  5. מעלה קוד לשרת עם rsync.

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

טיפ נאמפיי: החזרת אינדקסים מפונקציה

04/01/2021

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

import numpy as np

arr = np.arange(1, 11) * np.arange(1, 11).reshape(-1, 1)

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

In [6]: arr[0:3,0:3]
Out[6]: 
array([[1, 2, 3],
       [2, 4, 6],
       [3, 6, 9]])

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

def get_index():
    # THIS DOES NOT WORK
    return 0:3,0:3

print(arr[get_index()])

וזה מביא אותנו לטיפ היומי - הפונקציה המתאימה במקרה כזה נקראת slice ואני משתמש בה באופן הבא:

In [11]: def get_index():
    ...:     return (slice(0, 3), slice(0, 3))

In [12]: arr[get_index()]
Out[12]: 
array([[1, 2, 3],
       [2, 4, 6],
       [3, 6, 9]])

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

מה בננה

03/01/2021

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

console.log(('b'+'a'+ +'a'+'a').toLowerCase());

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

...

אז מה קרה כאן?

...

המפתח לתעלומה הוא העובדה שב JavaScript אופרטור הפלוס האונארי (Unary Plus) הוא דרך מתוחכמת לבצע המרה למספר. בגירסה פחות מסתורית של הקוד אפשר לכתוב כך:

console.log(('b' + 'a' + Number('a') + 'a').toLowerCase());

וזה כבר מתחיל להיות הגיוני כי ב JavaScript המרה של המחרוזת a למספר מחזירה את הערך המיוחד NaN שמשמעותו כמו שמו היא פשוט Not A Number (הגיוני, כי a אינו מספר):

> Number('a')
NaN

עכשיו נשאר רק להמיר את NaN למחרוזת ולהפוך לאותיות קטנות, והנה ה n-ים לבננה שלכם.