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

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

התנגדות

27/06/2019

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

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

אז מה עושים? הנה כמה טריקים שעוזרים לי, מוזמנים לנסות אותם וגם להוסיף את שלכם בתגובות:

  1. למצוא לקוח (או מאמן כושר), כלומר מישהו שישב לכם על הווריד עד שדברים יתקדמו.

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

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

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

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

תפסיקו להאשים את השפה

26/06/2019

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

https://www.destroyallsoftware.com/talks/wat

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

my $x = 'one';
my $y = 'two';

print($x + $y, "\n");

או שב Rails יש הבדל משמעותי בביצועים בין הפקודה count ל size, או על איך שבפייתון תחום ההגדרה של משתנים הוא פונקציה ולא בלוק ולכן הקוד הבא מדפיס 9 (אפילו שההדפסה כתובה אחרי הלולאה):

for i in range(10):
        pass

print(i)

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

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

בשביל מה צריך את זה?

25/06/2019

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

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

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

טיפ פייתון: החלפה והרצת קוד בתוך ביטוי רגולארי

24/06/2019

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

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

import re

def underscore_to_spaces(text):
    pat = re.compile('_')
    return pat.sub(' ', text)

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

בדיוק בשביל זה הפונקציה re.sub מספקת לנו גם נקודת התערבות בדיוק אחרי שהיא מוצאת התאמה ומאפשרת לנו להחליף את ההתאמה שנמצאה בתוצאת הפעלת פונקציה.

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

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

def almost_uppercase_words(text):
    pat = re.compile('_')
    return pat.sub(lambda m: m.group().capitalize(), text)

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

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

import re

def ucfirst_each_word(text):
    each_word = re.compile(r'(\w+)')
    return each_word.sub(lambda m: m.group().capitalize(), re.sub('_', ' ', text))

print(ucfirst_each_word("mad_mad_world"))

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

בניינים ודרכים

23/06/2019

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

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

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

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

old image of herzel street

שתים עשרה עצות מהירות לשיפור זמן הטעינה של האתר שלכם

22/06/2019

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

המשך קריאה

אין לי איך לעזור לך

21/06/2019

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

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

מה היה קורה אם במקום להגיד שנעשה משהו שכולם יודעים שלא נעשה נגיד במקום ״אין לי איך לעזור לך עם זה״:

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

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

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

איך הפסקתי לדאוג וזרקתי את כל ה Mock-ים לפח

20/06/2019

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

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

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

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

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

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

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

בנוסף הרבה מערכות היום מספקות מצב בדיקה שמיועד בדיוק בשביל שלא תצטרכו לכתוב עבורן Mock. מיילגאן מאפשרת לכם לשלוח מיילים לא במסגרת החבילה לנמעני בדיקות שהוגדרו מראש. כך גם Twilio שמאפשרת לעבוד עם כל ה API במצב בדיקה. בתשלומים פייפאל כוללת מצב Sandbox בו כל כרטיס אשראי עובר כדי שתוכלו לבדוק את מערכת התשלומים שלכם, ובפייסבוק אתם יכולים לפתוח Test User על פייסבוק עצמה כדי לבדוק את הקוד שלכם מול המערכת האמיתית.

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

הבחירה שהכי קשה להתמיד בה

19/06/2019

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

  1. הזרקות קוד - רובן מזוהות אוטומטית על ידי הכלי Brakeman

  2. מתקפות XSS - רובן מזוהות אוטומטית על ידי הכלי Brakeman, ונפתרות אוטומטית על ידי שימוש ב CSP.

  3. ניהול שבור של Sessions

  4. בעיית Insecure Direct Object Reference - נפתרת אוטומטית על ידי הג'ם cancancan וניהול הרשאות מסודר

  5. בעיית CSRF - ריילס כולל מנגנון שמופעל כברירת מחדל להגנה ממתקפה זו.

  6. הגדרות אבטחה לא נכונות (לדוגמא בסיס נתונים בלי סיסמא וכו'). ניתן לזהות בעזרת כלים אוטומטיים.

  7. איחסון לא מאובטח של סודות - ריילס כולל מנגנון לניהול סודות שמצפין אוטומטית את כל הסיסמאות.

  8. יצירת URL-ים סודיים שהכניסה אליהם לא מוגבלת - נפתר על ידי הג'ם cancancan וניהול נרשאות מסודר.

  9. הגנת HTTPS שלא מוגדרת טוב - ניתן לזהות באמצעות כלים אוטומטיים שרתים פגיעים.

  10. ביצוע Redirect לפי ערך שהגיע מהמשתמש - מזוהה אוטומטית באמצעות brakeman.

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

אנחנו יודעים טוב למה.

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

למי אכפת מה Critical Rendering Path

18/06/2019

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

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

הנה דוגמא קצרה להמחשה - נניח שיש לנו קובץ HTML שנראה כך:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <link href="https://fonts.googleapis.com/css?family=Rubik&display=swap" rel="stylesheet">

        <title>Demo</title>
<style>
p { font-family: 'Rubik', sans-serif; display: none; }
</style>
    </head>
    <body>
    <script src="demo.js"></script>
    </body>
</html>

והוא טוען קובץ JavaScript בשם demo.js שנראה כך:

setTimeout(function() {
  const div = document.createElement('div');
  div.style.fontFamily = 'Rubik';
  div.textContent = "hello world";
  document.body.appendChild(div);
}, 1000);

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

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

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

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

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

אם אתם רוצים ללמוד יותר על Critical Rendering Path ולראות דוגמאות למציאתו ושיפורו באתרים אמיתיים מוזמנים להצטרף אליי ביום חמישי לוובינר של שעה בנושא. הרישום בקישור:

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