ריחות קוד אינם באגים. תוכנית עם באגים קורסת, מחזירה תוצאות שגויות או נכשלת בבדיקה. תוכנית עם ריחות קוד עשויה לפעול בצורה מושלמת במשך שנים, אך כל שינוי בה עולה יותר ממה שהוא אמור להיות, כל תכונה חדשה נושאת סיכון בלתי צפוי, וכל ניסיון לבצע שיפוץ חושף תלויות שאף אחד לא ידע על קיומן. ריחות קוד הם המאפיינים המבניים של קוד שחוזים בעיות עתידיות: הם אינם גורמים לכשל מיידי, אך הם הופכים כל שינוי עתידי לקשה יותר, איטי יותר ומסוכן יותר ממה שהוא צריך להיות.
המונח זכה לפופולריות על ידי מרטין פאולר וקנט בק בספרו של פאולר, Refactoring: Improving the Design of Existing Code (1999), שקטלג 22 ריחות קוד בעלי שם והתאים כל אחד לטכניקת refactoring מתאימה. קטלוג זה נותר ההתייחסות הקנונית, והריחות שפאולר כינה, Long Method, God Class, Duplicated Code, Feature Envy, Divergent Change, Shotgun Surgery ואחרים, מופיעים במערכות כללים של SonarQube, כלי ניתוח סטטיים ורשימות תיוג לסקירת קוד ברחבי התעשייה כיום.
מהו ריח קוד?
ריח קוד הוא מאפיין פני שטח של קוד המקור שמרמז על בעיה מבנית או עיצובית עמוקה יותר. הקוד מתקמפל, עובר בדיקות ומפיק פלט נכון, אך משהו במבנה שלו מקשה על קריאתו, הרחבתו או שינויו בבטחה ממה שהוא אמור להיות. הגדרתו של פאולר: "אינדיקציה פני שטח שבדרך כלל מתאימה לבעיה עמוקה יותר במערכת".
ריחות קוד אינם הפרות באותו מובן כמו שגיאת תחביר או קביעה כושלת. הם אינדיקטורים, דפוסים שמפתחים מנוסים מזהים כסימני אזהרה, גם כאשר לא נראה כישלון מיידי. הסכנה היא שהם מצטברים: מתודה ארוכה אחת בבסיס קוד של 10,000 שורות היא אי נוחות קלה. מאות מתודות ארוכות, לוגיקה כפולה הפרושה על פני עשרות מודולים, ו-God Classes במרכז גרף התלות היא מערכת שהפכה קשה באמת לשינוי בטוח.
ריחות קוד לעומת באגים לעומת חוב טכני
שלושת המושגים הללו קשורים אך שונים, ובלבול ביניהם מוביל לתעדוף לקוי:
| מושג | הַגדָרָה | כישלון מיידי? | איך למצוא |
|---|---|---|---|
| חרק | קוד שמייצר התנהגות שגויה | כן, בדיקות נכשלות, משתמשים מדווחים על שגיאות | בדיקות, ניטור, יומני שגיאות |
| ריח קוד | דפוס מבני שחוזה בעיות עתידיות | לא, הקוד פועל כראוי | סקירת קוד, ניתוח סטטי |
| חוב טכני | העלות המצטברת של קיצורי דרך והחלטות גרועות מהעבר | לא, אבל מתערבב עם הזמן | מדדים, ניתוח מורכבות, הערכת מאמץ עיבוד מחדש |
ריחות קוד הם המנגנון שדרכו מצטבר חוב טכני. כל שיטה ארוכה שנוספת לבסיס הקוד היא יחידה של חוב טכני שנוצר; תשלום הריבית שלה הוא הזמן הנוסף שכל מפתח עתידי מקדיש להבנתה, וכל שינוי עתידי מקדיש להימנעות מתופעות הלוואי של גודלה.
מהו ריח קוד ב-SonarQube?
SonarQube מסווגת בעיות קוד לשלוש קטגוריות: באגים (שגויות בהחלט), פגיעויות (בעיות אבטחה) וריחות קוד (בעיות תחזוקה). ריחות הקוד של SonarQube ממפים ישירות לקטלוג של Fowler וכוללים כללים עבור מתודות ארוכות (מעל ספי שורות הניתנים להגדרה), בלוקים כפולים, יותר מדי פרמטרים, ציוני מורכבות קוגניטיבית מורכבים, טיפול בשגיאות חסר והפרות צימוד אדריכלי. כללי ריח הקוד ב-SonarQube הם המבצעיות האוטומטית הנפוצה ביותר בתעשייה של הטקסונומיה המקורית של Fowler.
ריחות הקוד של מרטין פאולר: הטקסונומיה הקלאסית
22 ריחות הקוד המקוריים של פאולר, המסודרים לפי קטגוריות, נותרו כבסיס הבסיסי. כל מערך הכללים של כלי ניתוח סטטי מרכזי נגזר מטקסונומיה זו.
| קטגוריה | ריחות קוד |
|---|---|
| נפיחות, קוד שגדל לגודל בלתי ניתן לתפעול | שיטה ארוכה, מחלקה גדולה, אובססיה פרימיטיבית, רשימת פרמטרים ארוכה, צבירי נתונים |
| ניצול לרעה של כיוון עצמים, שימוש לרעה בעקרונות OO | משפטי החלפה, שדה זמני, צוואה שנדחתה, מחלקות חלופיות עם ממשקים שונים |
| מונעי שינוי, להקשות על שינוי | שינוי מתפצל, ניתוח רובה ציד, היררכיות ירושה מקבילות |
| מוצרים מיותרים, קוד מיותר | הערות (מוגזמות), קוד כפול, מחלקה עצלה, מחלקת נתונים, קוד מת, כלליות ספקולטיבית |
| מצמדים, צימוד מוגזם | קנאה בתכונה, אינטימיות בלתי הולמת, שרשראות מסרים, איש ביניים |
הבנת הקטגוריה אליה משתייך ריח עוזרת לתעדף את התיקון: נפיחות ומניעת שינויים מתואמים ישירות עם עלות גבוהה של שיפוץ ריח; מצמדים מתואמים ישירות עם שבירות אדריכלית; ריח חד פעמי הוא הבטוח ביותר להסרה.
הקוד הנפוץ ביותר מריח: מדריך מהיר
| ריח קוד | כמו מה זה נראה | סיכון ראשוני |
|---|---|---|
| קוד משוכפל | אותו היגיון מופיע במספר מקומות | יש ליישם תיקוני באגים בכל מקום; עותקים משתנים עם הזמן |
| שיטה ארוכה | שיטות העולות על 20-30 שורות עם מספר אחריויות | עומס קוגניטיבי גבוה; קשה לבדוק התנהגות מבודדת |
| כיתה אלוהים / כיתה גדולה | כיתה אחת שעושה הכל | כל שינוי בתכונה נוגע לאותו מחלקה; התנגשויות מיזוג, שבריריות |
| רשימת פרמטרים ארוכה | שיטות שלוקחות 4+ פרמטרים | קל להעביר ערכים שגויים; קשה לקרוא אתרי שיחות |
| קנאה בתכונות | שיטה המשתמשת בנתונים של מחלקה אחרת יותר מאשר בנתונים שלה. | צימוד הדוק; שינוי במחלקה אחת שובר את השנייה |
| שינוי סוטה | כיתה אחת השתנתה מסיבות רבות ושונות | מפר אחריות יחידה; תופעות לוואי בלתי צפויות |
| ניתוח רובה ציד | שינוי אחד דורש עריכות במספר רב של מחלקות | עלות שינוי גבוהה; קל לפספס מופע |
| קוד מת | קוד שמעולם לא נקרא או מגיע אליו | מבלבל מפתחים; מצטבר לאורך שנים; מסבך את ההגירה |
| אובססיה פרימיטיבית | שימוש בסוגים בסיסיים (מחרוזות, ints) במקום אובייקטי דומיין | אימות מפוזר בכל מקום; יכולת הבעה ירודה |
| גושי נתונים | אותה קבוצת שדות מועברים יחד שוב ושוב | צריך להיות אובייקט תחום; מאותת על הפשטה חסרה |
| כלליות ספקולטיבית | קוד שנכתב עבור צרכים עתידיים מדומיינים | מורכבות מיותרת; אף אחד לא מבין למה היא שם |
| טיפול לא עקבי בשגיאות | תפיסות שקטות, אסטרטגיות חריגות מגוונות | כשלים אינם מתגלים; ניפוי שגיאות לוקח הרבה יותר זמן |
הגדרות ודוגמאות של ריח קוד
קוד משוכפל
ריח הקוד הנפוץ והיקר ביותר במערכות גדולות. שכפול נובע מפיתוח העתקה-הדבקה, מלחץ זמן ומצוותים שעובדים בממגורות ופותרים באופן עצמאי את אותה בעיה. התוצאה המיידית היא מס תחזוקה: כל שינוי בלוגיקה משותפת חייב להיות מיושם על כל עותק.
תאווה
// ServiceA -- discount calculation
double calculateDiscount(double amount) {
if (amount > 1000) return amount * 0.1;
return 0;
}
// ServiceB -- same logic, copied and forgotten
double computeDiscount(double value) {
if (value > 1000) return value * 0.1;
return 0;
}
כאשר כלל העסק משתנה (הסף הופך ל-1500, הקצב הופך ל-12%), עותק אחד מתעדכן והשני לא. שני מודולים אינם מסכימים כעת על לוגיקה עסקית בסיסית, והפער צף בייצור במהלך ביקורת ולא בבדיקות.
תיקון : לחלץ את הלוגיקה המשותפת לפונקציה אחת, מחלקת שירות או ספרייה משותפת ששני הקוראים מפנים אליה.
שיטה ארוכה
שיטה שגדלה מעבר לייעודה המקורי על ידי ספיגת אחריות נוספת לאורך זמן. העומס הקוגניטיבי של קריאת שיטה בת 200 שורות שונה מבחינה איכותית מקריאת עשרים שיטות בנות 10 שורות, לא רק מבחינה כמותית. קשה לבדוק שיטות ארוכות משום שהן מבצעות יותר מדי דברים לבדיקה בנפרד, וקשות להבנה משום שהקורא חייב להחזיק את כל הקשר הביצוע בזיכרון העבודה.
סף גילוי : שיטות מעל 20-30 שורות מצדיקות סקירה; מעל 50 שורות, שיפוץ כמעט תמיד מוצדק. ב-COBOL, פסקאות מעל 100 משפטים הן המקבילות.
פִּיתוֹן
class OrderProcessor:
def process_order(self, order):
# Validate order -- 40 lines
# Calculate discounts -- 30 lines
# Update inventory -- 25 lines
# Send notification emails -- 20 lines
# Generate invoice -- 35 lines
# 150+ lines total
pass
כל אחריות בשיטה זו צריכה להיות מחלקה או פונקציה נפרדת. איחודן פירושו שכל עדכון עתידי של חשבוניות, מלאי או הודעות עלול לערער את כל זרימת עיבוד ההזמנות.
כיתת אלוהים
מחלקה שצברה אחריות על פני מספר תחומים, ומפרה את עקרון האחריות היחידה בצורה כה חמורה עד שהיא הופכת למרכז הכובד של בסיס הקוד: הכל תלוי בה, ושינוי כל דבר בה דורש הבנה של הכל אודותיה.
אות זיהוי : מחלקה עם יותר מ-20-30 מתודות ציבוריות, או כזו ששמה מכיל "Manager", "Processor", "Handler", "Utils" או "Helper" המופעלת על מספר דומיינים שאינם קשורים.
שינוי סוטה
מחלקה שמשתנה מסיבות רבות ושונות שאינן קשורות. בכל פעם שסכמת מסד הנתונים משתנה, עליך לערוך מחלקה זו. בכל פעם שכללי התמחור משתנים, עליך לערוך מחלקה זו. בכל פעם שפורמט ההתראה משתנה, עליך לערוך מחלקה זו. למחלקה זו יש יותר מדי אחריות ויש לפצל אותה.
הגדרה : מחלקה אחת שמשתנה כל הזמן מסיבות שונות. ההפך מניתוחי רובה ציד.
ניתוח רובה ציד
שינוי קונספטואלי יחיד הדורש עריכות במגוון רחב של מחלקות. שינוי שיעור מס דורש שינוי חישוב backend, אימות frontend, טריגר מסד נתונים, משימת אצווה ושאילתת דיווח, בחמישה מקומות שונים. השמטת כל אחד מהם יוצרת התנהגות לא עקבית.
SQL
-- Tax logic duplicated across queries
SELECT amount * 0.05 FROM invoices;
SELECT amount * 0.05 FROM payments;
SELECT amount * 0.05 FROM reports;
שינוי 0.05 ל-0.07 דורש כעת מציאת כל מופע בקבצי SQL, פרוצדורות מאוחסנות וקוד אפליקציה.
קנאה בתכונות
מתודה שמבלה זמן רב יותר בשימוש בנתונים ובמתודות של מחלקה אחרת מאשר שלה. זה מאותת שההתנהגות כנראה שייכת למחלקה האחרת.
תאווה
// In ReportGenerator -- envious of Customer's data
double calculateCustomerRating(Customer customer) {
return customer.getOrderCount() * customer.getAverageOrderValue()
/ customer.getDaysSinceRegistration();
}
// This logic belongs in Customer, not ReportGenerator
קוד מת
קוד שקיים במאגר אך לעולם לא נקרא על ידי אף נתיב ביצוע בסביבת הייצור. קוד מת מצטבר לאורך שנים כאשר תכונות מוסרות, מוחלפות או מאורגנות מחדש מבלי למחוק את הקוד הישן. זה מוסיף רעש לסקירת הקוד, מבלבל את קליטת המפתחים לבסיס הקוד, מסבך את ניתוח ההגירה, ולעיתים מופעל מחדש בטעות.
איתורכלי ניתוח סטטי כולל SonarQube, Knip (עבור TypeScript/JavaScript), ו- SMART TS XL זיהוי פונקציות בלתי נגישות, מתודות שלא נקראו ומשתנים שאינם בשימוש בבסיס הקוד.
הפרות עקרון DRY
עקרון "אל תחזור על עצמך" (DRY) קובע שלכל פיסת ידע חייב להיות ייצוג יחיד וחד משמעי בתוך מערכת. הפרות DRY הן שורש הבעיה של קוד כפול, צבירי נתונים ותרחישים רבים של ניתוחי רובה ציד. כאשר לוגיקה עסקית מיוצגת במקומות מרובים, ייצוגים אלה באופן בלתי נמנע מתפצלים. DRY הוא העיקרון; קוד כפול הוא הריח שמצביע על ההפרה.
פִּיתוֹן
# DRY violation: same validation logic in three places
def validate_email_in_registration(email):
return "@" in email and "." in email
def validate_email_in_profile_update(email):
return "@" in email and "." in email
def validate_email_in_checkout(email):
return "@" in email and "." in email
# DRY-compliant: one function, three callers
def is_valid_email(email):
return "@" in email and "." in email
ספי גילוי: מתי קוד הופך לריח?
גילוי ריח בקוד דורש ספים מדידים. להלן המדדים הנפוצים והערכים המצביעים על ריח הדורש תשומת לב:
| מטרי | מה זה מודד | סף אזהרה | סף קריטי |
|---|---|---|---|
| מורכבות סיקלומטית | מספר ענפי החלטה בשיטה | מעל 10 | מעל 20 |
| אורך השיטה (בשורות) | מספר שורות בשיטה/פונקציה | מעל 20 | מעל 50 |
| ספירת פרמטרים | מספר הפרמטרים ששיטה מקבלת | מעל 4 | מעל 7 |
| אורך הכיתה | מספר השורות במחלקה | מעל 200 | מעל 500 |
| שיעור שכפול | אחוז הקוד המשוכפל | מעל 3% | מעל 10% |
| מורכבות קוגניטיבית | כמה קשה להבין את הקוד | מעל 15 | מעל 25 |
| צימוד אפרנטי (Ca) | מספר השיעורים התלויים בכיתה זו | מעל 15 | מעל 30 |
| צימוד אפרנטי (Ce) | מספר השיעורים בהם שיעור זה תלוי | מעל 15 | מעל 30 |
ספים אלה ניתנים להגדרה ב-SonarQube, ורוב פלטפורמות הניתוח הסטטי מאפשרות כללים מותאמים אישית המבוססים על מדדים אלה. מחלקות ושיטות הסף הקריטי הן מטרות השיפוץ בעלות העדיפות הגבוהה ביותר: הן המקורות הסבירים ביותר לפגמים עתידיים והרכיבים היקרים ביותר לתחזוקה.
כלי גילוי ריח קוד
זיהוי אוטומטי הוא הגישה היחידה הניתנת להרחבה לזיהוי ריחות קוד על פני בסיסי קוד גדולים. סקירה ידנית לוכדת חלק קטן ממה שכלים אוטומטיים מוצאים, וסקירה ידנית אינה מתאימה להרחבה למערכות מדור קודם עם מיליוני שורות.
| כלי | שפת אם | מה זה מזהה |
|---|---|---|
| סונאר קיוב / סונאר קלאוד | ג'אווה, פייתון, JS/TS, C# ועוד | טקסונומיית ריח מלאה של פאולר, נקודות חמות אבטחה, כפילויות |
| סגנון בדיקה + PMD | Java | הפרות סגנון, כפילויות, מדדי מורכבות |
| ESLint + typescript-eslint | JavaScript, TypeScript | פונקציות ארוכות, מורכבות, קוד שאינו בשימוש |
| פילינט + ראדון | פיתון | מורכבות, סגנון, מדד תחזוקה |
| רה-שרפר / רוכב | C# | קוד עודף, שיטות ארוכות, בעיות צימוד |
| Clippy | חלודה | הפרות אידיומטיות, דפוסים נפוצים שהם ריחות קוד ב-Rust |
| CodeClimate | ריבוי שפות | ציון מורכבות, שכפול, תחזוקה |
| SMART TS XL | COBOL, JCL, ג'אווה, פייתון, RPG, SQL, .NET | שכפול בין-לשוני, קוד מת, צימוד, סחף תלויות |
ריח של קוד בחלודה נתפסים בעיקר על ידי Clippy, אשר אוכף דפוסי Rust אידיומטיים. הריחות הנפוצים ביותר הספציפיים ל-Rust כוללים שיבוט מיותר, שימוש לרעה ב- unwrap() בנתיבי ייצור, ביטויי התאמה מקוננים יתר על המידה ופונקציות שצריכות להחזיר Result אבל להשתמש בפאניקה במקום.
ריחות קוד וחוב טכני: הקשר
חוב טכני הוא העלות המצטברת של החלטות עבר שהעדיפו מהירות על פני איכות. ריחות קוד הם המנגנון שבאמצעותו חוב זה מתבטא במבנה הקוד. הקשר הוא ישיר: כל ריח קוד שלא טופל הוא יחידה של חוב טכני, והריבית היא הזמן הנוסף שכל שינוי עתידי חייב להקדיש לעקיפה.
כפי שתואר בהקשר של ניתוח השפעה לניהול שינויי תוכנה , הבעיות המבניות שעליהן מצביעות ריחות קוד, צימוד מוגזם, לוגיקה כפולה, הצטברות קוד מת, מגדילות ישירות את היקף כל שינוי משום שהן מקשות על בידוד מה ישפיע שינוי נתון.
הסבר חוב טכני במונחים של ריחות קוד : אם בבסיס קוד יש 40% כפילויות, כל תיקון באג עולה פי 1.4 ממה שהוא אמור לעלות. אם מחלקת עיבוד הליבה היא מחלקת God שהכל תלוי בה, כל הוספת פיצ'ר דורשת הבנה ובדיקה של המחלקה כולה. אם טיפול בשגיאות אינו עקבי, כל אירוע ייצור דורש זמן חקירה נוסף מכיוון שאותות הכשל אינם אמינים. חוב טכני אינו מופשט, הוא סכום של חוסר היעילות המצטבר הזה.
מחקר CISQ מגלה באופן עקבי שמפתחים מבלים 30-40% מזמנם בעבודה סביב חוב טכני במקום בבניית פונקציונליות חדשה. צפיפות ריח הקוד היא המדידה הישירה ביותר לכמות החוב שנצברה.
איך SMART TS XL מזהה ריחות קוד בקנה מידה ארגוני
כלים בודדים כמו SonarQube ו-Clippy פועלים בתוך שפה אחת. בסביבות ארגוניות שבהן תוכניות COBOL כותבות למערכי נתונים ששירותי Java קוראים, שבהן זרמי משימות של JCL קוראים לתוכניות במספר שפות, וכאשר אותה לוגיקה עסקית שוכפלה באופן עצמאי על פני שלוש מערכות שונות שנכתבו בשלושה עשורים שונים, כלים בשפה אחת אינם יכולים לראות את התמונה המלאה.
SMART TS XL"S ניתוח קוד סטטי מזהה ריחות קוד בכל שפה בסביבה בו זמנית: לוגיקה כפולה בין ספר עותקים של COBOL לבין מחלקת תוכנה של Java, קוד מת בתוכניות RPG שאף משימת JCL לא מפעילה, תבניות מחלקות אלוהים בתוכניות COBOL שבהן פסקה אחת עושה את העבודה של חמישים, ודפוסי טיפול בשגיאות לא עקביים על פני גבול בין שפות.
יכולת מיפוי תלות היישומים מזהה את הריחות הארכיטקטוניים שכלים ברמת הקובץ אינם יכולים לראות: אילו רכיבים בעלי הצימוד האפרנטי הגבוה ביותר (הכי תלויים בהם, הסיכון הגבוה ביותר לשבירת דברים בעת שינוי), היכן קיימות תלות מעגליות בין מודולים שאמורים להיות בלתי תלויים, והיכן לוגיקה עסקית כפולה נשמרה באופן עצמאי במערכות שונות מבלי שאף אחד מהעותקים היה מודע לשני.
יכולת ניתוח ההשפעה הופכת ריחות לפעולה מעשית: לפני שינוי מחדש של כל רכיב בעל צימוד גבוה, ניתוח ההשפעה מונה כל רכיב תלוי שיש לבדוק, לאמת או לעדכן. זה הופך את "שיתוק השינוי מחדש" שחווים צוותים בבסיסי קוד גדולים ומלאי ריח לתוכנית תיקון מובנית ומוגדרת, שבה לכל שינוי יש היקף מוגדר ולא סיכון לא ידוע.
עבור צוותים המבצעים תוכניות מודרניזציה מדור קודם , ניתוח ריח קוד הוא הבסיס לתוכנית המודרניזציה: הקוד המת מוסר לפני תחילת ההגירה (מה שמפחית את ההיקף), הלוגיקה המשוכפלת מאוחדת למימושים קנוניים, הרכיבים בעלי הצימוד הגבוה ביותר עוברים מודרניזציה אחרונים (לאחר שכל מה שתלוי בהם טופל), ומחלקות ה-God מפורקות לפני שהן מומרות לשפה חדשה, מכיוון שהמרת מחלקת God לשפה חדשה יוצרת מחלקת God בג'אווה.
התמודדות עם ריחות קוד: מסגרת קביעת סדרי עדיפויות
לא כל ריח קוד מצדיק שיפוץ מיידי. הגישה הנכונה היא קביעת סדרי עדיפויות מבוססי סיכונים:
עדיפות 1, ריחות ברכיבים בעלי קצב שינוי גבוה. קוד שמשתנה לעתים קרובות ובעל מורכבות או צימוד גבוהים מייצר את מספר הפגמים הרב ביותר. רכיבים אלה עולים הכי הרבה לכל שינוי ומייצרים את מספר תקלות הייצור הרב ביותר. יש לתקן אלה תחילה.
עדיפות 2, ריח בגבולות האדריכליים. מחלקות אלוהים ורכיבים בעלי צימוד גבוה שהכל תלוי בהם הם המסוכנים ביותר לשינוי אך גם היקרים ביותר להשאירם ללא תיקון. אלה דורשים את ניתוח ההשפעה הקפדני ביותר לפני ביצוע שינוי פקטורינג.
עדיפות 3, קוד משוכפל מעבר לגבולות מערכת. כאשר אותה לוגיקה עסקית קיימת במספר מערכות, יש לתאם שינויים בין כל העותקים בו זמנית. איחוד כפילויות זו מפחית את תקורת התיאום ומונע סטייה.
עדיפות 4, הסרת קוד מת. קוד מת הוא הקטגוריה הבטוחה ביותר לטיפול: הסרתו לא יכולה לשבש התנהגות, אלא רק לחשוף תלויות שהוסתרו בעבר. יש להסירו לפני כל העברה או המרה כדי למנוע בזבוז מאמץ בהמרת קוד שלעולם לא ייקרא.
עדיפות 5, ריחות בסגנון ובמבנה באזורים בעלי סיכון נמוך. ניתן לטפל באופן אופורטוניסטי בשיטות ארוכות וברשימות פרמטרים בקוד יציב ובקצב שינוי נמוך, כאשר יש צורך לשנות קוד סמוך מסיבות אחרות, יש לבצע שינויים בריחות מסביב בו זמנית.
הדיסציפלינה של זיהוי, מדידה וטיפול בריחות קוד באופן שיטתי, ולא באופן ריאקטיבי, כאשר ריח כבר גרם לכשל ייצור, היא מה שמבדיל צוותי פיתוח ששומרים על מהירות אספקה לאורך זמן מאלה שמאטים בהדרגה ככל שהמערכות שלהם גדלות.