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


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

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

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

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

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

אז מה הדרך הנכונה היחידה לכתוב קוד?

במשפט אחד - תדאגו קודם ל״מה״ ורק אז ל״איך״.
מה הכוונה? עלינו להגיע אל נקודת ההרצה שלנו - (זו יכולה להיות פונקציית main אם זה סקריפט מסוים, אלה יכולים להיות טסטים אם מדובר על תשתית אוטומציה וזה יכול להיות rest api אם מדובר על קוד צד שרת) מאותה נקודת הרצה, נרצה לכתוב - בשפה העסקית ביותר מה הפעולה שאותה אנחנו רוצים לבצע.
דוגמה (תאורטית) של מחסנית קריאות יכול להמצא בפוסט על top-down programming.

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

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

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

למה בעיני דרכים אחרות לכתוב קוד לא נכונות

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

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

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

טוני רובינס אמר את זה יפה - .״הצבת מטרה היא הצעד הראשון בהפיכת הבלתי נראה לנראה״.

אבל...

בתחילת דרכי נהגתי (ויוכלו להעיד על כך חברי לצוות), להוציא את הדברים קצת מ context ולקחת קונספטים לקצה. אחרי שקראתי את clean code של uncle bob לצורך העניין, לא הייתי מסוגל לראות קוד שלא מפורק למיליון מחלקות, מתודות ושכל דבר מוזרק ב dependency injection וכו׳ וכו׳... זה נחמד מאוד ונשמע נכון בתאוריה, אבל בפועל, היו מקרים בהם השתמשתי בקונספט יתר על המידה וזה האט את קצב הפיתוח והתחזוקה במקומות בהם זה לא היה הכרחי.

משפט נחמד שנתקלתי בו ב PEP20 - The Zen of Python אומר:
Special cases aren't special enough to break the rules, although practicality beats purity.

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

לסיכום

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

וכמו הכל בחיים - ״Don't over do it".

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


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

תגובות

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

    השבמחק

הוסף רשומת תגובה

פוסטים פופולריים מהבלוג הזה

תכנות מונחה עצמים | Dependency Inversion Principle

מהם קבצי DLL ואיך להשתמש בהם?

מה ההבדל בין אוטומציה לפיתוח רגיל