על IEnumerable ו- IQueryable

הפעם אחדד נקודות על הממשקים IEnumerable ו- IQueryable ועל ההבדלים ביניהם.

נתחיל מהפשוט והמוכר יותר, IEnumerable. 
ממשק זה הוא בעצם מימוש בשפת C# של ה-Design Pattern הקרוי Iterator, המאפשר לנוע בתוך Collection איבר אחרי איבר.

Collection שמממש את הממשק הזה (לדוגמה List), מאפשר שימוש במשפטי foreach, כאשר אחזור האובייקטים יתבצע בצורה עצלה (Lazy) בצורה איטרטיבית.

אותו foreach יבקש בכל איטרציה איבר אחד בלבד, שיחזור מאותו Collection ע"י משפט Yield (או מימוש IEnumerator על הנתונים ואחזור שלהם).

למה צריך דבר שכזה?

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

    //If you will try to do ToList to this collection, it may take along time...    
  
    public class Top100PageCrawlerCollection : IEnumerable<object>
    {              
        public IEnumerator<objectGetEnumerator()
        {
            for(int i=0;i<100;i++)
                yield return DownloadPage();
        }
      
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
      
        public object DownloadPage()
        {
            System.Threading.Thread.Sleep(100);  
          
            return new Object();
        }
    }

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

    // If you will try to do toList to this collection, you will be stuck...
  
    public class PositiveNumbersCollection : IEnumerable<int>
    {  
        private int _innerseed = 0;
              
      
        public IEnumerator<intGetEnumerator()
        {
            while(true)
                yield return _innerseed++;
        }
      
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
      
    }

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


עכשיו נעבור ל-IQueryable: הבסיס מאחורי IQueryable הוא תמיכה בעצי ביטויים (Expression Trees). פעולות על Collection כגון פלטור לא יתבצעו על מאגר הנתונים, אלא ישמרו בטיפוס נתונים של ביטוי ורק ברגע ההרצה תתבצע פעולה הנקראת קמפול - הפיכה של אותו ביטוי לשאילתה הסופית שתרוץ אל מול הנתונים במקום המרוחק.

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

מבולבלים? דוגמה:

            DBCollection Db = new DBCollection();
          
            var query1 = Db.AsEnumerable().Where(x => x.Age < 50).Run();
          
            // SELECT * FROM Collection
          
            var query2 = Db.AsQueryable().Where(x => x.Age < 50).Run();
          
            // SELECT * FROM Collection WHERE age < 50

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

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

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

עידן

מותו של ה-Code Review

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

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

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

אז איך זה יכול להיות שאנחנו מוקפים בקוד לא טוב למרות שאנחנו מתמידים בתהליך ה-Code Review?

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

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

מה לגבי Code Review בפורמט שהוא מתבצע היום?
לדעתי הוא לא מספיק יעיל וטוב.

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

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

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

פתרון:

אני מציע Pair Programming חלקי. 

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

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

אם יש טייס וטייס משנה, למה אין מתכנת ומתכנת משנה? (שמתחלפים מדי פעם)

אז למה ציינתי Pair Programming חלקי? מה זה בעצם החלקי הזה?

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

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

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

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

תחשבו על זה,
עידן

למה אני פחות אוהב פונקציות Initialize ומה אפשר לעשות בנידון

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

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

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

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

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

var primeGenerator = new RandomGenerator();             
var num = primeGenerator.GetNext();

לעומת:

var primeGenerator = new RandomGenerator();      
primeGenerator.Initialize(seed:DateTime.Now); 
var num = primeGenerator.GetNext();


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

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

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

דמיינו שאתם מנסים למצוא את הסיבה לבאג ואתם מגלים שזה בגלל קוד מסובך ולוגי שמתחבא ב-Constructor, לא תתעצבנו?

אספקט נוסף הוא קריאה לפונקציות וירטואליות, שיכול להיות הרסני מאד ב-Constructor - דוגמה:

public class Base
{
        public Base()
        {
            Foo();
        }
  
        protected virtual void Foo() {}
    }

    public class Derived : Base
    {
        private object _object;
  
        public Derived()
        {
            _object = new object();
        }
              
        protected override void Foo()
        {
            _object.ToString();    //Null Reference!!!!
        }
    }

בגלל שסדר הקריאה ל- Constructors ביצירה של Derived הוא קודם כל ל- Constructor של Base, תהיה קריאה לפונקציה הוירטואלית שתגיע ישר למימוש שנמצא בתוך Derived ומשם נקבל Exception כיוון שעדיין לא בוצע אתחול ל-Object.

עוד חסרון בכתיבת קוד מורכב ב-Constructor - אולי לא שמתם לב או חשבתם על זה אף פעם, אבל אין ל-Constructor ערך מוחזר.
במידה והאתחול יכשל, אי אפשר יהיה להחזיר Null או false. המקסימום שנוכל לעשות הוא לזרוק Exception ולהשאיר את האובייקט שבקושי נולד במצב לא יציב.
חוץ מזה, מי רוצה לזרוק Exception ב-Constructor?
לצפות דבר שכזה, משמעו לעטוף קוד נוסף בבלוקים של Try וזה לא דבר יפה, צפוי או נכון במיוחד לעשות.

פתרונות -

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

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

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

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


     public sealed class SQLConnector : IDBConnector
    {
        internal SQLConnector(string connectionString)
        {
            Connect(connectionString);
        }
   
        private void Connect(string connectionString)
        {
            // Initialize Logic...
        }
   
        public void Query(string query)
        {
            //Query Logic, Must be connected
        }           
    }

    public class PersistencyFactory()
    {
        public IDBConnector CreateDBConnector(string connectionString)
        {
            //here we can return something, handle excpetions, insert more creation logic and return a valid object            
            return new SQLConnector(connectionString);
        }
    }

פתרון נוסף - אתחול סמוי רק כאשר צריך, מה שקרוי Lazy Loading.

    public class SQLConnector : IDBConnector
    {
        private string _connectionString;
        private bool _isConnected = false;
   
        public SQLConnector(string connectionString)
        {   
           _connectionString = connectionString;
        }
   
        private void Connect(string connectionString)
        {
            if(_isConnected) return;
       
            // Initialize Logic...
       
            _isConnected = true;
        }
   
        public void Query(string query)
        {
            Connect(_connectionString); //Lazy...
       
            //Query Logic, Must be connected or will get excpetion
        }           
    }

תהנו,
עידן



הבוליאני הרע

פרמטר בוליאני, דבר תמים, האמנם?

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

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

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

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



































פשוט, לא?

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

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

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

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












תהנו,
עידן

איך לכתוב בדיקות יחידה בצורה נכונה

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


גם מאמרים באינטרנט וגם אנשים לרוב מדברים על - Arrange-Act-Assert , שיטת שלושת השלבים למבנה של בדיקת יחידה.

אבל מה מעבר לזה? מה חשוב בקוד שכזה (חוץ משהבדיקה תעבוד)?

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

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

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

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

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

כן כן, שמעתם טוב - מותר לשכפל קוד.

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

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

בחזרה לעניין, נסו להבין מה הקוד זה עושה:






















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

ועכשיו תראו איזה קסם.
ככה הקובץ שלי נראה אחרי Refactor קל:




















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

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

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

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






























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

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

על הדרך תרוויחו את זה שתתאהבו במבחנים שאתם כותבים, תאמינו לי :)

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

ModuleName_ActionDescription_ExpectedResult

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

זהו,
עידן

סעיפי משמר + להכשל מהר

הפוסט של היום יעסוק ב-"סעיפי משמר", או באנגלית Guard Clauses.

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

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

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

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

הנה דוגמה קטנה:



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

 תחליטו בעצמכם מה יותר נקי ;)

עידן

פוסט ראשון

אז שלום,

אני עידן (לינקדאין), מהנדס תוכנה.

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

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

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

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

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

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

זהו.
תהנו.