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

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

אלף סיבות לא

02/07/2018

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

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

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

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

כוונות טובות

30/06/2018

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

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

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

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

הפיצ'ר הכי חשוב שפייתון שכחו לגנוב ל perl

29/06/2018

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

הספריה Moose של פרל מציעה תחביר מאוד נוח לכתיבת Data Objects:

use v5.18;
package User {
  use Moose;
  has 'name', is => 'rw', isa => 'Str', required => 1;
  has 'password', is => 'rw', isa => 'Str', required => 1;
}

my $u = User->new(name => 'ynonp', password => '1234');
say $u->name;

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

from dataclasses import dataclass

@dataclass
class User:
    name: str
    password: str

u = User(name='ynon', password='1234')

print(u.name)

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

use v5.18;
use HTTP::Tiny;
package User {
  use Moose;
  has 'name', is => 'rw', isa => 'Str', required => 1;
  has 'password', is => 'rw', isa => 'Str', lazy_build => 1;

  sub _build_password {
    my $response = HTTP::Tiny->new->get('https://makemeapassword.org/api/v1/passphrase/plain?pc=1');
    $response->{content};
  }
}

my $u = User->new(name => 'ynonp', password => '1234');
say $u->name;
say $u->password;

my $t = User->new(name => 'ynonp');
say $t->name;
say $t->password;
say $t->password;

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

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

מימוש redo ב Python

28/06/2018

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

לדוגמא הקובץ הבא:

I can see\
a tree behind\
the wall

And it's lovely

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

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

while (defined($line = <FH>) ) {
    chomp $line;
    if ($line =~ s/\\$//) {
        $line .= <FH>;
        redo unless eof(FH);
    }
    # process full record in $line here
}

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

זה עובד כי redo יודע לדלג ל"תחילת" הלולאה בלי לבצע את הקוד שבתוך ה while. הוא קצת דומה ל continue רק שמתחיל שורה אחת קדימה.

בפייתון אין redo אבל אולי אפשר להתקרב. גם כאן תחילה הקוד ואחריו ההסבר:

def redo_loop(iterable):
    fake_next = None
    def redo(n):
        nonlocal fake_next
        fake_next = n

    def read_next():
        nonlocal fake_next
        return next(iterable)

    while True:
        try:
            if fake_next is not None:
                val = fake_next
                fake_next = None
            else:
                val = next(iterable)

            yield val, redo, read_next
        except StopIteration:
            break


for line, redo, read_next in redo_loop(iter(fileinput.input('demo.txt'))):
    line = line.strip()
    if line.endswith('\\'):
        line = line[:-1] + ' '
        line += read_next()
        redo(line)
        continue
    print(line)

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

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

הפונקציה read_next שחוזרת גם היא מ redo_loop עוקפת את הבעיה שלא תמיד יש לנו בגוף הלולאה את ה Generator Object עליו אנחנו רצים. בשביל לקרוא את השורה הבאה מהקובץ צריך לדעת משהו על fileinput ועל ה File Object שעכשיו שמור בו. אומנם ב fileinput אפשר להגיע לאוביקט זה (באמצעות שדה _state של fileinput) אבל זה לא נכון במקרה הכללי וגם כאן לא בטוח שרצוי להסתמך עליו.

האם הייתי משתמש ב redo הזה בעולם האמיתי? כלל לא בטוח. אבל אני חושב (מקווה?) שהדוגמא הזאת תעזור לכם לחשוב מזווית נוספת על Generator Functions בפייתון ואולי תיתן לכם רעיונות לדברים מדליקים נוספים שאפשר לממש עם פיצ'ר זה.

לכתוב קוד כאילו שיש זמן

27/06/2018

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

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

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

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

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

https://www.tocode.co.il/workshops/33

תוכנית וובינרים לחודש יולי

26/06/2018

הי חברים,

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

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

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

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

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

כל הרשימה וכפתורי ההצטרפות בעמוד הוובינרים בכתובת: https://www.tocode.co.il/workshops

נתראה, ינון

ארכיטקטורה

25/06/2018

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

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

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

זוזו הצידה, אני יודע BFS

24/06/2018

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

המשך קריאה

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

23/06/2018

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

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

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

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

הבאג המבלבל של השבוע

22/06/2018

פרס הבאג המבלבל של השבוע חייב ללכת לתוכנית הבאה:

members = ['one', 'two', 'three', 'four', 'five']
count = 0

for item in members:
    if item[0] == 't':
        count =+ 1

print(count)

ראיתם את הבעיה? אם לא קחו רגע לחשוב על זה. במקום לספור כמה מילים מתחילות ב t ולהדפיס 2 התוכנית מדפיסה תמיד 1.

וזה ברור כשעוצרים לקרוא את הקוד.

השורה:

count =+ 1

אולי חוקית בפייתון, אבל בשביל לספור צריך להשתמש ב:

count += 1

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