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

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

חדש באתר: קורס git

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

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

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

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

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

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

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

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

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

https://www.tocode.co.il/bundles/git

נתראה שם, ינון

הודעות קומיט מוצלחות (ומוצלחות פחות)

05/09/2018

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

$ git log --oneline -10

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

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

  1. היא מסבירה מה השינוי שבוצע
  2. היא מסבירה למה ביצענו את השינוי
  3. היא לא מבלבלת

נתחיל עם הסעיף השלישי כי אותו הכי קל להבין.

המשך קריאה

מנגנונים שקשה לבדוק

04/09/2018

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

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

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

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

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

  3. נוכל להחליף את S3 ואת שרת המיילים ב Mock Objects במערכת מהם יהיה לנו הרבה יותר קל לקרוא את המידע ולראות שהמידע אכן התקבל ונשלח כמו שצריך.

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

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

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

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

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

הזמנה לוובינר: פיתוח משחקון ב Python

03/09/2018

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

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

המשך קריאה

הבעיה עם הערות בקוד

02/09/2018

קצת בדומה לשלט הזה:

Imgur

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

שתי גישות לתיקון באג

01/09/2018

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

  def expired?
    expires_at = DateTime.new(card_validity_year.to_i, card_validity_month.to_i, 1, 0, 0, 0) + 1.month
    Time.zone.today > expires_at
  end

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

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

  def expired?
    expires_at = Date.new(card_validity_year.to_i, card_validity_month.to_i, 1, 0, 0, 0) + 1.month
    Time.zone.today > expires_at
  end

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

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

require 'rails_helper'

describe PaymentToken do
  describe '#expired?' do
    it 'should return true when card is expired' do
      p = PaymentToken.new(card_validity_year: 1.year.ago, card_validity_month: 1)
      expect(p.expired?).to be(true)
    end
    it 'should return false when card is not expired' do
      p = PaymentToken.new(card_validity_year: 1.year.from_now, card_validity_month: 1)
      expect(p.expired?).to be(false)
    end
  end
end

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

טעינת קובץ CSS בצורה אסינכרונית

31/08/2018

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

<!DOCTYPE html>
<html>
  <head>
    <title>Hello CSS</title>
    <link rel='stylesheet' href='startup.css' />
    <link rel='stylesheet' href='all.css' />
  </head>

  <body>
    <h1>Hello CSS</h1>
    <div class='fold'>
      <p>consider this text which will only appear after the larger stylesheet file is loaded</p>
    </div>
  </body>
</html>
body {
  background: orange;
}

.fold {
  display: none;
}
.fold {
  display: block;
}

p {
  font-size: 1.5em;
  line-height: 2;
}

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

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

<!DOCTYPE html>
<html>
  <head>
    <title>Hello CSS</title>
    <link rel='stylesheet' href='startup.css' />

      <script>
        var link = document.createElement('link');
        link.rel='stylesheet';
        link.href = 'all.css';
        document.head.appendChild(link);
      </script>
      <noscript>
        <link rel='stylesheet' href='all.css' />
      </noscript>
  </head>

  <body>
    <h1>Hello CSS</h1>
    <div class='fold'>
      <p>consider this text which will only appear after the larger stylesheet file is loaded</p>
    </div>
  </body>
</html>

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

<link rel="preload" href="mystyles.css" as="style" onload="this.rel='stylesheet'">

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

היכרות עם Stimulus.js

30/08/2018

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

המשך קריאה

עריכה

29/08/2018

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

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

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

מה תעשו כשהקוד שלכם יתיישן?

28/08/2018

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

מה תעשו כשזה יקרה?

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

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

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

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

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

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

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