שלום אורח התחבר

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

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

הזינו את כתובת המייל וקבלו את הפוסט היומי בכל בוקר אליכם לתיבה:

3 מטרות

בואו נדבר על 3 מטרות של קורסים:

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

  2. הקניית ידע - הקורס מלמד איך להשתמש בכלי מסוים.

  3. שיפור מיומנות (טכניקה).

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

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

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

חדש מאתר ToCode: תוכנית המנויים

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

אל תבלעו Exceptions. זה לא טעים.

התקנתי Capybara לפני כמה ימים (או יותר נכון ניסיתי להתקין). כל פעם שהפעלתי את המערכת אחרי ההתקנה הופיעה ההודעה הבאה:

System test integration requires Rails >= 5.1 and has a hard dependency
on a webserver and `capybara`, please add capybara to
your Gemfile and configure a webserver
(e.g. `Capybara.server = :webrick`) before attempting to use system tests.

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

לפני שנגיע לזה בואו נלך לקוד של rspec-rails, הספריה ממנה הגיעה הודעת השגיאה. כך נראה הבלוק הבעייתי:

      included do |other|
        begin
          require 'capybara'
          require 'action_dispatch/system_test_case'
        # rubocop:disable Lint/HandleExceptions
        rescue LoadError
          # rubocop:enable Lint/HandleExceptions
          abort """
            System test integration requires Rails >= 5.1 and has a hard
            dependency on a webserver and `capybara`, please add capybara to
            your Gemfile and configure a webserver (e.g. `Capybara.server =
            :webrick`) before attempting to use system tests.
          """.gsub(/\s+/, ' ').strip
        end

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

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

# from: https://www.python.org/download/releases/2.2.3/descrintro/
class defaultdict(dict):

    def __init__(self, default=None):
        dict.__init__(self)
        self.default = default

    def __getitem__(self, key):
        try:
            return dict.__getitem__(self, key)
        except KeyError:
            return self.default

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

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

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

נ.ב. 2 - אצלי Capybara לא עלה בגלל חוסר תאימות בין ריילס 5.1 ל Capybara 3. ברגע ששינמכתי ל Capybara 2 הכל התחיל לעבוד.

נ.ב. 3 - קפיברה (בנוסף להיותה המכרסם הגדול ביותר) היא ספריית בדיקות אוטומטיות ב Ruby.

קוד כניסה (פייתון)

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

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

import sys
class CodePanel:
    def __init__(self, code):
        self.code = code
        self.guess = Buffer(len(code))

    def check(self):
        if self.guess.get_as_string() == self.code:
            print('welcome')
            sys.exit(0)
        else:
            print('wrong code. try again')
            self.guess = Buffer(self.guess.size)

    def type_char(self, ch):
        self.guess.write(ch)
        if self.guess.is_full():
            self.check()

class Buffer:
    def __init__(self, size):
        self.buf = [''] * size
        self.at = 0
        self.size = size

    def write(self, ch):
        self.buf[self.at] = ch
        self.at += 1

    def is_full(self):
        return self.at == self.size

    def get_as_string(self):
        return ''.join(self.buf)


panel = CodePanel('13579')
code = '123413579'
for ch in code:
    panel.type_char(ch)

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

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

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

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

1
1, 2
1, 2, 3
1, 2, 3, 4
1, 2, 3, 4, 1
3, 2, 3, 4, 1
3, 5, 7, 9, 1

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

import sys

class CircularBuffer:
    def __init__(self, size):
        self.size = size
        self.buf  = [''] * size
        self.at   = 0

    def write(self, ch):
        self.buf[self.at] = ch
        self.at = (self.at + 1) % self.size

    def get_as_string(self):
        res = []
        for i in range(self.size):
            res.append(self.buf[(self.at + i) % self.size])
        return ''.join(res)

class CodePanel:
    def __init__(self, code):
        self.code = code
        self.guess = CircularBuffer(5)

    def check(self):
        if self.guess.get_as_string() == self.code:
            print(f'welcome: {self.guess.get_as_string()}')
            sys.exit(0)

    def type_char(self, ch):
        self.guess.write(ch)
        self.check()


panel = CodePanel('13579')
code = input()
for ch in code:
    panel.type_char(ch)

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

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

class CircularBuffer:
    def __init__(self, size):
        self.size = size
        self.buf  = deque([''], size)
        self.at   = 0

    def write(self, ch):
        self.buf.append(ch)

    def get_as_string(self):
        return ''.join(list(self.buf))

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

ציד באגים

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

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

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

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

חרדיות בהייטק

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

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

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

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

חרדת ביצוע

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

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

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

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

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

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

תיקונים

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

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

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

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

ונעבור לחלק המקצועי

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

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

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

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

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

משוררים ושרברבים

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

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

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

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

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

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

tr -cs A-Za-z '\n' |
tr A-Z a-z |
sort |
uniq -c |
sort -rn |
sed ${1}q