uk
Feedback
ToCode

ToCode

Відкрити в Telegram

טיפים קצרים למתכנתים מאת ינון פרק

Показати більше
1 419
Підписники
Немає даних24 години
Немає даних7 днів
+130 день
Архів дописів
ToCode
1 419
# שימו לב: נוד קורא גם את ה gitignore שלכם כשמפרסמים חבילות ישבתי היום אחר הצהריים לפרסם חבילה לפרויקט. בדרך כלל אני לא עובד אחרי הצהריים כי הילדים פה, אבל היו כמה דברים שרציתי לסיים וחשבתי שאני יכול לנסות קצת מולטי טאסקינג. מפה לשם אני מנסה לבנות את החבילה ולפבלש, ובחלון אחר מפעיל npm install כדי לראות שזה עובד. החבילה עצמה היא פרויקט צד לקוח שבניה שלו מייצרת תיקיה בשם dist, ובחלון השני שמשתמש בחבילה יש קוד שעושה import מאותה dist. בקיצור בחזרה לבניה מפעיל publish בחלון אחד ו install בחלון השני ו... אין dist בספריה שהותקנה. נו, בטח שכחתי להפעיל build בין כל הילדים שקופצים פה מסביב. ניסיון שני בבניה, שוב publish, שוב install ו... dist עדיין לא שם. בסופו של דבר ברחתי מהילדים למקום שקט, הסתכלתי בשקט בספריה ומצאתי שם קובץ .gitignore עם השורה:
dist/
גיט לוג חשף שהיתה כוונה טובה מאחורי השורה הזאת - אין צורך להוסיף את כל תוצרי הבניה ל git. אבל היא באה עם באג: בלי קובץ .npmignore בתיקיה, באופן אוטומטי npm יתעלם מהקבצים ב .gitignore ולא יפרסם אותם עם החבילה שלכם. וכמו תמיד אחרי שמבינים את הבעיה הפיתרון היה פשוט ליצור קובץ .npmignore ריק, ולהפסיק להאשים את הילדים בבאגים שלי...

ToCode
1 419
# טיפ טייפסקריפט: הפקודות Parameters ו ReturnType בעבודה עם טייפסקריפט, במיוחד בעבודה עם קוד חיצוני, אנחנו נתקלים בחתימות מסובכות של פונקציות וצריכים לכתוב קוד שמתייחס אליהן - לדוגמה אני רוצה לכתוב פונקציה שלי שעוטפת פונקציה חיצונית. עדיין כללי מדי? בואו ננסה דוגמה ספציפית עם טייפסקריפט וריאקט. אני רוצה לכתוב קומפוננטה שמציגה טקסט, וגם סופרת כמה פעמים שיניתי את הטקסט. בשביל זה יהיה לי נוח להחזיק את הטקסט במשתנה סטייט, להחזיק משתנה סטייט נוסף שסופר כמה פעמים הטקסט השתנה, ולכתוב פוקנציה שמעדכנת את שניהם יחד. ב JavaScript רגיל הייתי יכול לכתוב משהו כזה:
function setTextAndCount(stuff) {
    setText(stuff);
    setCount(c => c + 1);
}
וזה עובד יופי ומתמודד עם שתי החתימות של setText, גם אם stuff הוא פונקציה וגם אם stuff הוא ערך קבוע. בתרגום ל TypeScript הפשטות הולכת לאיבוד. פתאום אני צריך לספר לטייפסקריפט מה סוג המשתנה שאני צריך לקבל, ובשביל זה צריך לדעת מה סוג המשתנה בסטייט ולהגדיר Union Type שמורכב מהמשתנה בסטייט או מפונקציית העדכון שלו. לא נעים. במקום זה אני יכול להשתמש ב Parameters כדי לקבל את רשימת הפרמטרים של פונקציה מסוימת ולהשתמש בה בתור טיפוס. ובתור בונוס אני משתמש ב ReturnType כדי לקבל את סוג ערך ההחזר של פונקציה מסוימת. התרגום עם שני אלה נראה ככה:
function setTextAndCount(
  ...args: Parameters<typeof setText>
): ReturnType<typeof setText> {
  setText(...args);
  setCount((c) => c + 1);
}
דוגמת קוד מלאה? בטח למה לא: https://codesandbox.io/s/restless-night-u4ukmn?file=/src/App.tsx:186-347 <iframe src="https://codesandbox.io/embed/restless-night-u4ukmn?fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="restless-night-u4ukmn" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" ></iframe>

ToCode
1 419
אם אתם כבר מנויים לאתר תוכלו לגלוש לקורס ולהתחיל כבר ללמוד דוקר, ואם עדיין לא מנויים תוכלו להתחיל את הפרק הראשון מהקורס שפתוח לקהל הרחב ואחרי זה להירשם כמנויים כדי להמשיך את כולו. קישור לקורס דוקר: https://www.tocode.co.il/bundles/docker מקווה שתהנו מהקורס ינון

ToCode
1 419
# קורס Docker ו Kubernetes אחרי יותר מדי זמן והרבה התלבטויות על התכנים, אני שמח להשיק כאן את קורס Docker של טוקוד. הנה מה שתמצאו בפנים וקצת רכילות מאחורי הקלעים. ## מה בקורס ב 2013 סלומון הייקס מסתכל על העולם ורואה שחור בעיניים: מתכנתים נאבקים במחשבים כדי להעביר את הקוד שלהם ממכונת הפיתוח לפרודקשן, אנשי QA מעבירים דיווחים על באגים והמתכנתים פשוט זורקים "זה עובד אצלי על המחשב" ושרתים שלוקח נצח לקנפג ואף אחד לא מעז לגעת בהם. סלומון הייקס, אגב, היה באותו זמן מייסד של חברת ענן בשם dotCloud, שאיפשרה למתכנתים להעלות מערכות שהם כתבו לענן (קצת כמו הרוקו הוותיקה מהם). הייקס ראה טוב מאוד כמה קשה למתכנתים להעביר את המערכות שלהם לענן של דוטקלאוד ובאותו זמן הוא ראה את היכולות הטכנולוגיות של ה Linux Kernel והבין שהוא עלה על סטארט אפ חדש. המוצר שהייקס התחיל לפתח אז וממשיך לפתח עד היום נקרא Docker. דוקר הוא כלי שמאפשר לי לקחת מערכת שכתבתי, לסגור אותה ב"קופסה" ולהעביר אותה למכונה אחרת. מה שמיוחד בדוקר זה שהוא מצליח לקחת את כל הקבצים שרלוונטיים לאותו יישום שכתבתי לתוך הקופסה - כולל ספריות של מערכת ההפעלה, כלי שורת פקודה שהשתמשתי בהם, ואפילו את מערכת ההפעלה עצמה. זה לא מכונה וירטואלית כי הביצועים הם כמו של תוכנית רגילה, אבל זה הכי קרוב שנגיע מבחינת העברה אמינה של קוד בין מכונות. קורס Docker פה באתר מחולק ל-4 פרקים: 1. בחלק הראשון לומדים איך להשתמש בדוקר משורת הפקודה ואיך להשתמש ב"קופסאות" שאחרים יצרו בשבילנו. תלמדו על המאגר Dockerhub ותראו איך עם דוקר אפשר להפעיל כל תוכנה שאנחנו צריכים ומכל גירסה בלי להתקין אותה ובלי שהיא תתנגש עם דברים שכבר יש לי על המחשב. 2. בחלק השני לומדים איך להשתמש ב Docker Compose כדי לבנות סביבת פיתוח משודרגת. בגלל שלא חייבים יותר להתקין דברים על המכונה שלי, אנחנו נראה איך להקים סביבות בקלות ולמשל להוסיף בלחיצת כפתור שרת Redis, Mongo או פוסטגרס למערכת שלי. 3. בחלק השלישי כבר לוקחים צעד קדימה לכיוון החיים בענן. אנחנו נלמד איך לבנות אימג'ים ו Dockerfile-ים משלנו כדי שנוכל לשתף את העבודה שלנו עם אחרים. 4. ובחלק הרביעי נראה איך להעלות את כל מה שכתבנו לקלאסטר שרתים בענן באמצעות תוכנת Kubernetes. אם אתם מתכנתים שצריכים לעבוד בסביבה שבה משתמשים בדוקר - הקורס ייתן לכם הבנה טובה של דוקר וכל הכלים שסביבו וילמד אתכם להשתמש בשורת הפקודה בצורה יעילה. בסיום הקורס כבר לא תצטרכו עזרה מאף אחד כשקונטיינר לא עולה לכם או בשביל לכתוב קובץ Dockerfile. אם אתם עובדים על פרויקט שלכם היכרות עם דוקר וקוברנטיס תאפשר לכם לפתח אותו תוך שילוב תהליכי אוטומציה ב Deployment ולהינות מהכלים המתקדמים בתעשיה בהעברת הקוד לפרודקשן. בסיום הקורס יהיו לכם תשובות טובות לשאלות הבאות: 1. מה זה אומר לרוץ בתוך קונטיינר, מה המגבלות של הקונטיינר. 2. איך לעבוד עם Docker משורת הפקודה. 3. איך לעבוד עם docker-compose בצורה יעילה - כולל ניהול של מספר סביבות עבור בדיקות ועבור Staging. 4. איך לשמור מידע רגיש באמצעות Secrets. 5. מה קורה כשקונטיינר עולה? מה זה Entrypoint Script ומה ההבדל בינו לבין Dockerfile? איך מחכים שסרביסים מסוימים יהיו זמינים לפני שקונטיינר עולה, ואיך מריצים קוד איתחול בעליה של קונטיינרים 6. איך קונטיינרים מתקשרים אחד עם השני ועם העולם החיצון. ## מה השארתי בחוץ בעבודה על הקורס התחלתי עם חלום מאוד גדול לייצר קורס שמלמד עבודה בסביבת Micro Services ובענן. דוקר הוא כמובן כלי עבודה מרכזי בסביבה זו, אבל חוץ ממנו יש עוד אינסוף טכנולוגיות שאנחנו משתמשים בהן בכתיבת מוצר מבוזר מבוסס Micro Services, טכנולוגיות כמו GraphQL, syslog, oauth, Message Queues. במקביל אנשי Devops בכל ארגון מייצרים תהליכי אוטומציה באמצעות כלי CI, כלים כמו Github Actions או Jenkins, שמטפלים בכל התשתית שמעבירה קוד ממכונה למכונה. במהלך העבודה על הקורס הבנתי שלקשור את כל הטכנולוגיות האלה לקורס אחד, במקום להוסיף לקורס רק פוגע בלכידות הפנימית שלו. וככה לאט לאט הורדתי כל פעם עוד נושא, עד שנשארתי עם מהלך פשוט וחשוב - מדוקר לקוברנטיס, או מפיתוח לפרודקשן. בתקופה הקרובה אני מתכנן להעביר מספר וובינרים על פיתוח בסביבת Micro Services ועל הקמת אוטומציות. כבר בחודשים הקרובים מתוכננים וובינרים על RabbitMQ ועל Auth0, וברבעון הבא אני מתכנן לשלב וובינר על Github Actions והקמת אוטומציה של Deployment באמצעותו. אם תהיה התעניינות אולי ארחיב חלק מהנושאים האחרים לקורסים משלהם.

ToCode
1 419
# טיפ דוקר: סדר פעולות ב Dockerfile קובץ ה Dockerfile הבא מגדיר בניה של אפליקציית Rails שגם מכילה קוד צד לקוח, שבתורו מתקמפל עם node.js:
FROM ruby:2.7

WORKDIR /app
COPY . .

ENV RAILS_ENV=production
ENV NODE_ENV=production

RUN gem install bundler:2.2.5
RUN sh -c "apt-get update && apt-get install -y nodejs npm"

RUN bundle install

RUN sh -c "cd client; npm install"
RUN "./bin/rails assets:precompile"

CMD ["./bin/rails", "s"]
רואים מה הבעיה כאן? ## סדר פעולות ושכבות הפקודות RUN, COPY ו ADD של דוקר יוצרות כל אחת שיכבה חדשה. ברגע שדוקר מסיים ליצור שכבה הוא שומר אותה ב Cache, וכשמפעילים פעם נוספת build דוקר יוכל להשתמש בעותק השמור - בתנאי שלא היה שינוי בשיכבה או בשכבות שמעליה. בקוד הדוגמה הפעולה:
COPY . .
יוצרת שיכבה חדשה, אבל זו שיכבה מאוד לא יציבה. כל שינוי הכי קטן בקובץ בתיקיית הפרויקט יגרום ל Cache להיות לא רלוונטי, ולדוקר לזרוק את השיכבה ואת כל השכבות שהוא בנה מעליה, וליצור את הכל מחדש. זה אומר שעם קובץ Dockerfile כזה אתם תצטרכו לחכות הרבה זמן כל פעם שבונים מחדש את הפרויקט. תיקון מאוד קל לבעיה יהיה לשנות את סדר הפעולות כך שכל שיכבה תיווצר הכי מאוחר שרק אפשר. זה יראה כך:
FROM ruby:2.7

WORKDIR /app

ENV RAILS_ENV=production
ENV NODE_ENV=production

RUN gem install bundler:2.2.5
RUN sh -c "apt-get update && apt-get install -y nodejs npm"

COPY . .
RUN bundle install

RUN sh -c "cd client; npm install"
RUN "./bin/rails assets:precompile"

CMD ["./bin/rails", "s"]
העליתי למעלה את שתי פקודות ה RUN הכלליות יותר (התקנת bundler והתקנת node ו npm), כך שהן נמצאות עכשיו מעל ה COPY. מכאן דוקר יוכל לשמור ב Cache את המכונה אחרי התקנת bundler, node ו npm, ולהתקין רק את הפרויקט שלי כל פעם שמשהו משתנה.

ToCode
1 419
# שלוש סיבות למה לא כדאי להכריח פריימוורקס לעבוד כמו שאתם רוצים כשריילס התחיל לצבור תאוצה הרבה דיברו על כמה הפריימוורק טוב אם אתה עובד כמו שריילס התכוונו שתעבוד, וכמה אתה הולך לסבול אם שיטת העבודה שלו שונה. לימים, ריילס אכן הפך גמיש יותר, אבל הכלל הישן תקף עדיין ובכל פריימוורק שנלמד: אם מצאת טריק מתוחכם לגרום לפריימוורק להתנהג אחרת ממה שכותביו התכוונו, אולי עדיף לאחסן את הטריק הזה במגירה ולמצוא דרך אחרת (או פריימוורק אחר). אלה הסיבות שבגללן אני מעדיף לא להכריח פריימוורקס לעבוד כמו שאני רוצה: 1. אין אחריות (או יותר נכון אין עזרה מגוגל) - מרגע שהתחלנו לשבור את הפריימוורק כדי להתאים אותו לצרכים שלנו, יהיה קשה מאוד להיעזר בגוגל כדי למצוא ולתקן באגים. ה Use Case שלנו יהיה שונה ואי אפשר לדעת איזה באגים הגיעו בגלל השינוי שלנו, איזה באגים נובעים באמת מטעויות ושימוש לא נכון, ואיזה באגים באמת קיימים בפריימוורק בלי קשר אלינו. 2. מחר זה יישבר - אם אני משנה פריימוורק לעבוד כמו שאני רוצה היום, אף אחד לא מבטיח לי שבגירסה הבאה של הפריימוורק החריץ דרכו הכנסתי את השינוי שלי עדיין יהיה שם. קוד שמייצר סכנה שבגללו אי אפשר יהיה לשדרג גירסה זה קוד מסוכן. 3. המנגנון החדש שלי לא יהיה עקבי עם מנגנונים אחרים קיימים של הפריימוורק, ולפעמים ישבור אותם. רק בגלל שאני חושב שמנגנון מסוים צריך לעבוד בפריימוורק לא אומר שהוא באמת עובד או שדברים צריכים להיות קלים כמו שנדמה לי. כשאני בונה פיצ'ר ומרגיש שהכלים עובדים נגדי, כדאי לקחת את הרמז - או להבין איך להשתמש בכלי בצורה נכונה, או להחליף את הכלי.

ToCode
1 419
# היום למדתי: הסלקטור focus-within סלקטור מאוד שימושי ב CSS שגיליתי רק לאחרונה הוא :focus-within. הוא מאפשר לזהות מתי פוקוס נמצא על אלמנט או על אחד הילדים שלו. ולמה זה טוב? נניח שאנחנו בונים קומפוננטה מורכבת ב HTML/CSS. בואו נגיד שזו תיבת טקסט לבחירת תגיות, שכשלוחצים על התיבה מופיעה רשימה של כל התגיות לבחירה. מבחינת הפוקוס, לחיצה על תיבת הטקסט נותנת לה את הפוקוס, ואחרי זה לחיצה על כל אחד מהפריטים מעבירה את הפוקוס לפריט שנלחץ. אם תיבת הטקסט ורשימת הפריטים נמצאים בתוך אותו div, אז אני יכול להשתמש ב focus-within על הדיב העוטף הראשי כדי להחליט אם להציג או להסתיר את רשימת האפשרויות. בקוד ה HTML יראה כך:
<div class="multiselect">
  <label>
      Click To Select:
      <input type="text" />
  </label>

  <ul>
    <li><label tabindex="0"><input type="checkbox" />item 1</label></li>
    <li><label tabindex="0"><input type="checkbox" />item 2</label></li>
    <li><label tabindex="0"><input type="checkbox" />item 3</label></li>
    <li><label tabindex="0"><input type="checkbox" />item 4</label></li>
    <li><label tabindex="0"><input type="checkbox" />item 5</label></li>
  </ul>
</div>

<div class="multiselect">
  <label>
      Click To Select:
      <input type="text" />
  </label>

  <ul>
    <li><label tabindex="0"><input type="checkbox" />item 1</label></li>
    <li><label tabindex="0"><input type="checkbox" />item 2</label></li>
    <li><label tabindex="0"><input type="checkbox" />item 3</label></li>
    <li><label tabindex="0"><input type="checkbox" />item 4</label></li>
    <li><label tabindex="0"><input type="checkbox" />item 5</label></li>
  </ul>
</div>

<div class="multiselect">
  <label>
      Click To Select:
      <input type="text" />
  </label>

  <ul>
    <li><label tabindex="0"><input type="checkbox" />item 1</label></li>
    <li><label tabindex="0"><input type="checkbox" />item 2</label></li>
    <li><label tabindex="0"><input type="checkbox" />item 3</label></li>
    <li><label tabindex="0"><input type="checkbox" />item 4</label></li>
    <li><label tabindex="0"><input type="checkbox" />item 5</label></li>
  </ul>
</div>
וה CSS יהיה:
.multiselect:focus-within {
  background: lightblue;
}

.multiselect ul {
  display: none;
}

.multiselect label {
  display: block;
  width: 100%;
}

.multiselect:focus-within ul {
  display: block;
}
והתוצאה live בקודפן היא: <iframe height="300" style="width: 100%;" scrolling="no" title="Untitled" src="https://codepen.io/ynonp/embed/xxpKyze?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> See the Pen <a href="https://codepen.io/ynonp/pen/xxpKyze"> Untitled</a> by Ynon Perek (<a href="https://codepen.io/ynonp">@ynonp</a>) on <a href="https://codepen.io">CodePen</a>. </iframe> נסו ללחוץ על תיבה כדי לראות רשימת אפשרויות שמופיעה מתחתיה, ואז סמנו חלק מהאפשרויות ותראו שהרשימה עדיין מוצגת.

ToCode
1 419
# בעולם האמיתי נניח שיש לכם שתי רשימות ממוינות בפייתון ואתם צריכים לייצר מהן רשימה שלישית שתהיה ממוינת גם היא. כלומר בהינתן:
a = [10, 20, 33, 51, 94, 100]
b = [2, 3, 99, 102, 500]
צריך להדפיס את הרשימה:
[2, 3, 10, 20, 33, 51, 94, 99, 100, 102, 500]
נו, מה הבעיה אתם אומרים, אנחנו בפייתון, אז אפשר לכתוב פשוט:
def test1():
    return sorted([*a, *b])
וזה עובד עד שאיזה קול בראש מתחיל לנג'ס שמה פתאום אני מחבר את שתי הרשימות וממיין את התוצאה כששתי הרשימות הגיעו ממוינות, ולמה לא להתאמץ קצת ולרוץ במקביל על שתי הרשימות לפי סדר המיון, כלומר משהו כזה:
def test2():
    i = 0
    j = 0
    output = []
    while True:
        if i >= len(a):
            output.extend(b[j:])
            break

        if j >= len(b):
            output.extend(a[i:])
            break

        next_a = a[i]
        next_b = b[j]

        if next_a < next_b:
            i += 1
            output.append(next_a)
        else:
            j += 1
            output.append(next_b)

    return output
ואז בדיוק כשאני עף על עצמי על הפיתרון המתוחכם שכתבתי ואיזה מתכנת על מהחלל אני שיודע לרוץ בלולאה על שתי רשימות במקביל, אני רוצה לקחת את זה עוד צעד קדימה ומחליט למדוד ביצועים רק בשביל לראות כמה זמן מעבד העבודה שלי חסכה. ונו אנחנו בפייתון אז גם את זה אפשר בקלות:
print("Short solution took - ", timeit.timeit("test1()", globals=locals()))
print("Long smart solution tool - ", timeit.timeit("test2()", globals=locals()))
והתוצאה הלא ממש מפתיעה:
Short solution took -  0.26264833300956525
Long smart solution tool -  1.3138192089973018
יש הרבה סיבות שהקוד השני מפשל, אבל הן לא העיקר כאן. יותר חשוב לשים לב שביצועים זה לא עניין של אינטואיציה ומה נראה לי יותר מוצלח. במקום לבזבז את הזמן על פיתרונות מתוחכמים, עדיף לוודא קודם שבאמת יש בעיה, ובכל מקרה לעשות קומיט רק אחרי שאתם בטוחים שהפיתרון המתוחכם משפר את המצב.

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

ToCode
1 419
# הלימוד הכואב יש שני סוגים מרכזיים של לימוד- 1. הלימוד הקל זה כשאתה לומד משהו שקודם לא ידעת. לדוגמה היום למדתי ש"יעה" ברוסית זה "סאבוק". הנה עכשיו גם אתם יודעים את זה. 2. הלימוד הקשה זה כשאתה לומד משהו חדש שצריך להחליף ידע קיים. לי זה קרה לדוגמה כשגיליתי שיש הגבלה על גודל מסמך ב MongoDB. ברגע אחד הבנתי שאולי יש בעיה בכל הקוד שכתבתי שמתקשר עם MongoDB בכל פרויקט שאי פעם כתבתי. לא רציתי לקבל את זה. דוגמה יותר קשה היתה כשיותר ויותר לקוחות שלי עברו מ perl ל Python. זה לקח לי שנתיים להבין שאנשים מעדיפים Python על פני perl. זה לקח לי עוד שנתיים אחרי זה להתחיל להעדיף בעצמי את פייתון על פני פרל. 90% מהזמן לא הושקע בלימוד פייתון אלא בניסיון מנטלי לדחות את מה שהעולם ניסה להגיד לי. המוח האנושי לא אוהב שמזיזים לו את הגבינה. כשיש פריט מידע חדש שלא מסתדר עם דברים שכבר ידענו, נעשה הכל כדי לפסול את המידע החדש. וההרגל הזה יכול לעלות לנו ביוקר, כמתכנתים וכעובדים בכלכלה המודרנית. החדשות הטובות הן שאפשר להתאמן ולשנות את זה. הצעד הראשון הוא לזהות את השיעורים הקשים כשהם עומדים מולנו, להסתכל עליהם בסקרנות ולשאול - מה אני יכול ללמוד ממה שקורה עכשיו? מה היה קורה אם הייתי בוחר בחירה אחרת? איזה עלות שקועה גורמת לי להמשיך להתווכח? ומה צריך לקרות כדי שאוכל לקבל את המידע החדש ולהתקדם איתו?