שלום אורח התחבר

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

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

מעדיפים לקרוא מהטלגרם? בקרו אותנו ב:@tocodeil

או הזינו את כתובת המייל וקבלו את הפוסט היומי בכל בוקר אליכם לתיבה:

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

אחר כך הגיעו בדיקות אוטומטיות ו CI ובכלל היינו בגן עדן: במקום להעסיק אנשי QA יקרים שיעשו את אותו דבר שוב ושוב, כתבנו סקריפטים של בדיקות ונתנו לשרת ה CI/CD להריץ את כל הבדיקות ולזרוק לפרודקשן את הקוד שעבר.

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

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

החסרונות של השיטה ברורים:

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

  2. הבדיקה עלולה בעצמה לשבור משהו בבסיס הנתונים או בשרת.

  3. הבדיקה מגלה את הבעיה מאוחר מדי, אחרי ה Deployment במקום לפניו.

  4. הבדיקה לא יכולה "לרמות", כלומר להכניס נתונים ספציפיים למערכת כדי לקבל תוצאה צפויה.

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

מחר בעשר בבוקר אני וגבור סבו נעביר וובינר (באנגלית הפעם, כי גם לי מתחשק לגוון) על כתיבת בדיקות לאתר קיים והרצתן בסביבת פרודקשן. אנחנו מתכננים לקחת את אתר meetup.com, לבנות עבורו תוכנית בדיקות ולממש כמה שנספיק ממנה, וכמובן להריץ את הבדיקות משרת בדיקות נפרד דרך Github Actions. אם בא לכם להצטרף כצופים או לעזור לנו בכתיבה מוזמנים להירשם בקישור: https://code-maven.com/selenium-with-python-live.

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

נתראה, ינון.

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

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

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

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

  3. סרביס הזמנות ותשלומים.

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

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

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

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

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

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

  4. הוא יצטרך לתקשר עם סרביס הסטטיסטיקות כל פעם שמשתמש מבצע פעולה באתר.

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

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

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

הפיתרון לכל הבעיות נקרא API Gateway. זה לא כלי אלא תבנית פיתוח, ולמעשה יש המון כלים שיכולים לתפקד בתור API Gateway, והרבה פעמים נראה מערכות שמשלבות כמה מהם. בתבנית API Gateway אנחנו בונים שירות בכניסה למערכת שיחבר את כל הסרביסים שלנו, ובגלל זה לפעמים הוא נקרא גם Backend for Frontend.

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

אבל יש עוד כמה דברים ש API Gateway יכול לעשות:

  1. ה Gateway יוסיף "מזהה בקשה" לכל בקשה שנכנסת למערכת, וכל הסרביסים משתמשים במזהה זה בכתיבה ללוג. כך קל לנו להסתכל בלוג ולהבין איזה בקשות קשורות, גם בין סרביסים שונים.

  2. ה Gateway יוכל לטפל ב Caching של תשובות כדי להוריד עומס מהסרביסים הפנימיים.

  3. ה Gateway יוכל לטפל ב Rate Limit או לחסום בוטים.

  4. אם חלק מהסרביסים למטה, ה Gateway הוא במקום טוב לזהות את זה מהר ולהעביר את העומס לסרביסים אחרים.

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

רק בגלל שהחלטתם להשתמש ב API Gateway אל תחשבו שאתם נעולים לכלי אחד ספציפי או יחיד במערכת. אין בעיה להרים שרת NGINX או HAProxy בכניסה למערכת כדי לטפל ב Rate Limit, ומאחוריו שרת Express כדי לבצע Request Aggregation. הדבר החשוב הוא להסתכל על ה Gateway בתור סרביס שיכול להיות מורכב מכמה תחנות ואחראי על תחומי עבודה מסוימים.

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

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

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

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

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

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

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

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

http-request deny if { path -i -m beg /api/ } { src 10.0.0.0/16 }

תעצור בקשות שמגיעות מהרשת 10.0.0.0/16 ומיועדות לנתיב שמתחיל ב /api. או השורה הזאת:

redirect scheme https if !{ ssl_fc }

תשלח ללקוח HTTP Redirect אם הוא לא פנה ב HTTPS.

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

core.register_fetches("dow", function(txn)
  return os.date("*t").wday
end)

מייצר ל Haproxy פונקציה חדשה בשם dow שמחזירה את היום בשבוע (מתחיל מ-1 ליום ראשון).

עכשיו נניח ששמרתי את קוד ה lua מהדוגמה בקובץ /usr/local/etc/haproxy/dow.lua, ונניח שיש לי שני שרתי אפליקציה על הרשת, אחד בשם web שמציג את התוכן האמיתי שלי והשני בשם closed שמציג דף שאומר שהאתר סגור כעת; אז אני יכול לכתוב את קובץ הגדרות ה haproxy.cfg הבא כדי לסגור את האתר בסופי שבוע:

global
  log /dev/log  local0
  log /dev/log  local1 notice
  user haproxy
  group haproxy
  daemon
  lua-load /usr/local/etc/haproxy/dow.lua

defaults
  log global
  mode  http
  timeout connect 10s
  timeout client 30s
  timeout server 30s
  option  httplog
  option  dontlognull

frontend default
  bind :80
  use_backend closed if { lua.dow() eq 6 } || { lua.dow() eq 7 }
  default_backend web

backend web
  server s1 web:80

backend closed
  server s1 closed:80

הבלוק המעניין הוא הבלוק frontend ובפרט השורות:

use_backend closed if { lua.dow() eq 6 } || { lua.dow() eq 7 }
default_backend web

הפונקציה lua.dow היא פונקציית ה lua שכתבתי שמחזירה את היום בשבוע, והשורה בבלוג הגלובל:

lua-load /usr/local/etc/haproxy/dow.lua

היא שהיתה אחראית לטעינת קובץ ה lua וציון הנתיב אליו.

  • "איך אתה ב git?"
  • "מסתדר. יודע ליצור ענפים, לעשות קומיטים, למזג. מה שצריך אני עושה ומקסימום כשאני נתקע אני תמיד מוצא ב Stack Overflow את הדרך"

  • "איך אתה ב Java?"

  • "ב Java אני די טוב. כתבתי יישומי צד-שרת גדולים וגם תיקנתי בעיות ביצועים ונעילות ב DB. אני יודע לכתוב Design Patterns ומבין טוב ארכיטקטורה. אין לי בעיה להיכנס לכל מערכת ולכתוב איזה פיצ'ר שצריך".

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

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

אבל אפשר לחשוב על כיוון אחר לגיוס, שהתשובה אליו היא לא על סקלאה:

  • "ובאיזה שפת תכנות את ממש מצטיינת?"

  • "מה את יודעת על שפת התכנות הזאת שרק מעטים יודעים?"

  • "איזה דבר גילית על שפת התכנות הזאת שממש הפתיע אותך?"

  • "איזה הרצאה הכי אהבת מהכנס המקצועי האחרון שהיה על טכנולוגיה זו? ולמה?"

  • "איך היית ממליצה למישהו חדש ללמוד את הטכנולוגיה הזו מאפס?"

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

  • "ומה עשית כדי להכיר כל כך טוב את הטכנולוגיה הזו?"

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

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

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

הפקודה:

$ aws dynamodb list-tables

מדפיסה JSON עם רשימת כל הטבלאות:

{
    "TableNames": [
        "Table1",
        "Table2",
        "Table3"
    ]
}

נשלח אותו ל jq כדי לקבל רק את השמות:

 AWS_PAGER="" aws dynamodb list-tables | jq '.TableNames[]'
"Table1"
"Table2"
"Table3"

ונמשיך עם xargs בשביל למחוק אחת אחת:

$ AWS_PAGER="" aws dynamodb list-tables | jq '.TableNames[]'| xargs -n 1 aws dynamodb delete-table --table-name

האם היה נחמד לקבל ממשק GUI יותר נוח מאמזון? ברור. ועדיין עדיף ללמוד לעבוד עם ה CLI מאשר לבזבז שעה על מחיקת אוביקטים וניווט ב GUI.

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

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

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

והאופליין? כמות המיטאפים שמתוכננים החודש עם הכותרת "מפגש פרונטאלי ראשון אחרי הקורונה" וכמות האנשים שבאים אליהם מזכירה לנו שהרבה יותר קל לנו ליצור קשרים עם אנשים כשאנחנו איתם באותו מרחב פיזי, ושאנשים רבים התגעגעו לחוויה הזאת ומחפשים אותה. אם גם לכם זה היה חסר שווה להעיף מבט בלינק https://www.meetup.com/cities/il/tel_aviv-yafo/events/ ולראות אם יש אירוע שמעניין אתכם ומסתדר בשעות.

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

function MyProjects () {
  const { data: user } = useSWR('/api/user')
  if (!user) {
    return 'loading...';
  }

  const { data: projects } = useSWR('/api/projects?uid=' + user.id)
  if (!projects) return 'loading...'

  return 'You have ' + projects.length + ' projects'
}

וזה חלום כי ה useSWR השני הוא בהפעלה "מותנית", לפעמים הוא יבוצע ולפעמים לא, וזה אסור לפי חוקי ה Hooks.

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

function MyUser() {
  const { data: user } = useSWR('/api/user')

  if (!user) {
    return 'loading...';
  }

  return (
    <MyProjects user={user} />
  );
}

function MyProjects () {
  const { user } = props;
  const { data: projects } = useSWR('/api/projects?uid=' + user.id)

  if (!projects) return 'loading...'

  return 'You have ' + projects.length + ' projects'
}

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

אופציה שניה וטובה יותר היא מה שעשו ב SWR ומה ששווה לבנות גם ב Hooks שלכם: הכנסת הלוגיקה לתוך ה Hook. ב SWR אפשר לכתוב את אותו הקוד כך:

function MyProjects () {
  const { data: user } = useSWR('/api/user')
  const { data: projects } = useSWR(() => '/api/projects?uid=' + user.id)
  // When passing a function, SWR will use the return
  // value as `key`. If the function throws or returns
  // falsy, SWR will know that some dependencies are not
  // ready. In this case `user.id` throws when `user`
  // isn't loaded.

  if (!projects) return 'loading...'
  return 'You have ' + projects.length + ' projects'
}

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

  if (typeof key === 'function') {
    try {
      key = key()
    } catch (err) {
      // dependencies not ready
      key = ''
    }
  }

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

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

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

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

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

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

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

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

המשך קריאה...