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

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

ואולי דווקא יש פיתרון קל

07/12/2021

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

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

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

טיפ JavaScript: בדיקת פרמטרים והודעות שגיאה

06/12/2021

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

function init(options={}) {
  let redisHost = 'localhost', redisPort = 6379;

  if (options.redis && options.redis.host) {
    redisHost = options.redis.host;
  }
  if (options.redis && options.redis.port) {
    redisPort = options.redis.port;
  }
  console.log(`Connecting to redis at ${redisHost}:${redisPort}`);
}

init();
init({ redis: { host: 'my-redis' } });

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

init({ redis: { host: process.env.REDS_HOST } });

במצב כזה למרות שהעברתי ערך למשתנה redis.host הפונקציה תתעלם מהערך בגלל שהוא undefined ותשתמש בערך ברירת המחדל. שום הודעת שגיאה לא תופיע ולא יהיה לנו שום רמז שכתבנו משתנה בשגיאת כתיב.

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

function init(options={}) {
  let redisHost = 'localhost', redisPort = 6379;

  if (options.redis) {
    if ('host' in options.redis) {
      if (options.redis.host) {
        redisHost = options.redis.host;
      } else {
        throw 'Error: options.redis.host is undefined';
      }
    }
    if ('port' in options.redis) {
      if (options.redis.port) {
        redisPort = options.redis.port;
      } else {
        throw 'options.redis.port is undefined';
      }
    }
  }

  console.log(`Connecting to redis at ${redisHost}:${redisPort}`);
}

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

למי קראת בינוני?

05/12/2021

בציוץ שעורר סערה לא קטנה בשעתו לורי ווס כתב את הדברים הבאים:

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

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

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

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

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

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

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

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


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

  1. הוא לומד מטעויות ומשתמש בניסיון לטובתו.

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

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

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

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

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

אם אני צריך לבחור בין אדריכל שממציא בניינים חדשים לבין אדריכל שמראה Track Record של בניינים אמינים שבנה ומצליח לתקשר לי את כל השיקולים והבחירות בבניה - אני בוחר את השני לבנות כל בניין שאני צריך.

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

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


נ.ב. אחרון בגיזרת הקונטקסט. למי שלא מכיר אז הבחור המצייץ הוא Co-Founder של npm (כן מערכת ניהול החבילות ל Node.JS, זאת שמחזיקה את מאגר החבילות הגדול בעולם). הוא מתכנת ווב מאז 1996 ולפני npm הקים חברה בשם awe.sm שנמכרה ל Unified. הוא הרצה ב JS Conf וכנסים נוספים וכותב בלוג טכני מוצלח כבר 20 שנה. יום אחד נצטרך לשאול את עצמנו איך אנשים מעולים מהתעשיה נתקעים עם רגשי נחיתות לא מוצדקים.

לא שמתי לב

03/12/2021

לא שמתי לב לגודל של ה Docker Image ... עד שהתחיל להיגמר לי המקום על הדיסק.

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

לא שמתי לב לאיטיות השאילתות ב DB ... עד שקוד התחיל להיכשל על Timeouts בדיוק בשיא העומס על המערכת.

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

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

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

מדריך: תמיכה במספר סביבות קוברנטס עם Kustomize

02/12/2021

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

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

המנגנון המובנה ב kubectl לניהול שינויים בין סביבות נקרא kustomize. בפוסט זה אציג דוגמה פשוטה לפרויקט שמשתמש ב kustomize כדי לייצר קבצי yaml שונים לסביבות השונות.

המשך קריאה

מצאו את ההבדלים

01/12/2021

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

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

(בגיט יש מנגנון מובנה בשם git bisect שבנוי בדיוק על הרעיון הזה)

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

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

חלומות על עננים

30/11/2021

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

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

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

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

  1. כל שירותי הענן צריכים לעבור סטנדרטיזציה. לא צריך 5 דרכים שונות לאחסן קובץ. אני חולם על ממשק אחד מבוסס CLI שיעבוד לכל העננים בצורה זהה.

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

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

  4. אנחנו צריכים דרך לשתף spec שלם של מערכת על ענן. משהו כמו helm chart או docker compose עבור כל אותם אינסוף ממשקים של AWS או Azure או GCP או מה שלא תרצו.

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

ומה אתכם? איזה חלומות יש לכם על הענן?

קיים בשפה

29/11/2021

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

a = [1, 2, 3]
b = ['a', 'b', 'c']
c = ['@', '!', '#']

אז הפעלה של zip על שלושת הרשימות תחזיר רשימה אחת של שלושה איברים שכל אחד מהם מורכב מאיבר אחד מכל אחת מהרשימות, כלומר:

>>> list(zip(a, b, c))
[(1, 'a', '@'), (2, 'b', '!'), (3, 'c', '#')]

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

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

a = [1, 2, 3, 4, 5, 6]
b = ['a', 'b', 'c']
c = ['@', '!', '#']

אז התוצאה של ה zip תישאר בדיוק אותו דבר - והאיברים העודפים ב a פשוט לא יקבלו טיפול.

עכשיו תשמעו מה היה לבראנט בושר להגיד על זה:

It is clear from the author's personal experience and a survey of the standard library that much (if not most) zip usage involves iterables that must be of equal length.

ובהמשך אותו מסמך:

In fact, the author has counted dozens of other call sites in Python's standard library and tooling where it would be appropriate to enable this new feature immediately.

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

ופה יש שני דברים שצריך לשים לב אליהם:

  1. כל אחד היה יכול לעטוף את zip בפונקציה חדשה (נקרא לה safezip) שבודקת שאכן כל רשימות הקלט באותו האורך. מעטים אם בכלל עשו את זה.

  2. (וזה ניחוש שלי) - אחרי התוספת ל zip בפייתון 3.10, הרבה אנשים יתחילו להעביר את הפרמטר החדש ואפילו יסתכלו מוזר על אלה שלא מעבירים אותו.

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