2.15 डिबगिंग और समस्या निवारण
अवलोकन और प्रेरणा
डिबगिंग वह अनुशासित काम है जिससे यह पता लगाया जाता है कि कोई सिस्टम वह क्यों कर रहा है जो उसे नहीं करना चाहिए, और समस्या निवारण वही कौशल है जो समय के दबाव में एक चलते हुए उत्पादन सिस्टम पर लागू किया जाता है। दोनों दोषों पर लागू वैज्ञानिक पद्धति हैं: आप एक आश्चर्यजनक व्यवहार देखते हैं, उसके कारण के बारे में एक परिकल्पना बनाते हैं, एक ऐसा प्रयोग डिज़ाइन करते हैं जो उसकी पुष्टि या खंडन करे, और अपनी अटकल नहीं बल्कि सबूत को यह बताने देते हैं कि क्या बदलना है। इस तरह से किया गया, डिबगिंग एक सीखने योग्य, सिखाने योग्य इंजीनियरिंग कौशल है। लोककथा के रूप में किया गया, यह अंधविश्वास बन जाता है: बेतरतीब लाइनें बदलना, सर्वर पुनः आरंभ करना, और उम्मीद करना।
एक बड़ी टीम के लिए, यह अंतर महंगा है। एक भी कठिन दोष कई सेवाओं में इंजीनियरों को खींच सकता है, ऑन-कॉल घंटे खपा सकता है, और एक रिलीज़ को रोक सकता है। जब हर व्यक्ति सहज ज्ञान से डिबग करता है, तो वह प्रयास नहीं बढ़ता, क्योंकि कोई भी यह पुनरुत्पादित या समझा नहीं सकता कि दूसरे ने क्या आज़माया। जब टीम एक तरीका साझा करती है (पहले पुनरुत्पादन, खोज द्वारा पृथक्करण, बग को एक असफल परीक्षण में पकड़ना, फिर ठीक करना), तो वही प्रयास एक दोहराने योग्य प्रक्रिया और एक बढ़ते हुए रिग्रेशन सूट में बदल जाता है। डिबगिंग परीक्षण रणनीति (अध्याय 2.4), सॉफ़्टवेयर गुणवत्ता (अध्याय 2.11), और उन निर्माण आदतों (अध्याय 2.9) से कसकर जुड़ी है जो कोड को पहली जगह में निदान योग्य बनाती हैं।
एंटरप्राइज़ और सरकारी सेटिंग्स में, दांव बढ़ जाते हैं। एंटरप्राइज़ दोष सेवा और टीम सीमाओं को पार करते हैं, इसलिए लक्षण देखने वाला व्यक्ति शायद ही वह व्यक्ति हो जो कारण का मालिक है। सरकारी सिस्टम ऐसे प्रतिबंध जोड़ते हैं जिनका अधिकांश इंजीनियर कभी सामना नहीं करते: एयर-गैप्ड या प्रतिबंधित वातावरण जहाँ आप प्रोडक्शन पर डिबगर संलग्न नहीं कर सकते, प्रतिकृत निर्माण जिन्हें आर्टिफ़ैक्ट से निदान किया जाना चाहिए, और ऑडिट ट्रेल जिन्हें यह रिकॉर्ड करना चाहिए कि आपने क्या और क्यों बदला। तीनों में, लक्ष्य समान है: अटकल की जगह सबूत लाना।
मुख्य सिद्धांत
- सिद्धांत बनाने से पहले पुनरुत्पादित करें। एक बग जिसे आप मांग पर ट्रिगर नहीं कर सकते वह एक अफवाह है, दोष नहीं।
- डिबगिंग परिकल्पना परीक्षण है। बताएं कि आप क्या मानते हैं, फिर सबसे सस्ता प्रयोग डिज़ाइन करें जो आपको ग़लत साबित कर सके।
- पहले त्रुटि और स्टैक ट्रेस पढ़ें। सिस्टम आमतौर पर आपको यह बताता है कि लाइन बदलने से पहले वह कहाँ टूटा।
- समस्या क्षेत्र को खोजें, स्कैन न करें। ऊपर से नीचे पढ़ने के बजाय हर कदम में संदिग्ध क्षेत्र को आधा करें।
- न्यूनतम तक कम करें। मामले को तब तक छोटा करें जब तक केवल आवश्यक ट्रिगर न बचे।
- एक बार में एक बदलाव। शॉटगन एडिट उस सबूत को नष्ट कर देते हैं जो बताता कि कौन सा बदलाव मायने रखता था।
- ठीक करने से पहले बग को एक असफल परीक्षण में पकड़ें। फ़िक्स तभी साबित होता है जब वह परीक्षण हरा हो जाए और हरा बना रहे।
- निकटतम लक्षण नहीं, मूल कारण खोजें। एक पैच जो लक्षण छुपाता है वह दोष को वापस लौटने देता है।
सिफ़ारिशें
कुछ भी बदलने से पहले दोष को विश्वसनीय रूप से पुनरुत्पादित करें
आपका पहला काम एक विश्वसनीय पुनरुत्पादन है: चरणों का एक सेट या एक स्वचालित केस जो मांग पर बग को ट्रिगर करता है। इसके बिना आप एक वास्तविक फ़िक्स को संयोग से अलग नहीं बता सकते, क्योंकि लक्षण ऐसे कारणों से आ-जा सकता है जिन्हें आपने कभी नियंत्रित नहीं किया। इनपुट, वातावरण, संस्करण, और समय को स्थिर करें। यदि बग रुक-रुक कर आता है, तो उस छिपे हुए चर की खोज करें जो इसे प्रकट करता है (एक विशिष्ट डेटा रिकॉर्ड, एक घड़ी सीमा, एक समवर्ती रिक्वेस्ट) जब तक पुनरुत्पादन विश्वसनीय न हो जाए। एक विश्वसनीय पुनरुत्पादन डिबगिंग में सबसे मूल्यवान एकल आर्टिफ़ैक्ट है, क्योंकि उसके बाद सब कुछ मापने योग्य हो जाता है।
कोड को छूने से पहले त्रुटि, लॉग, और स्टैक ट्रेस पढ़ें
एक भी सिद्धांत बनाने से पहले, वह पढ़ें जो सिस्टम आपको पहले ही बता चुका है। स्टैक ट्रेस (विफलता के क्षण में कॉल चेन का रिकॉर्ड) आमतौर पर वह फ़ाइल, लाइन, और क्रम बताता है जो विफल हुआ। अपवाद संदेश, उसके आसपास की लॉग लाइनें, और स्कोप में मौजूद मान आपके कुछ भी बदलने से पहले खोज को संकुचित करते हैं। इंजीनियर उन कारणों पर सिद्धांत बनाने में घंटे बर्बाद करते हैं जिन्हें ट्रेसबैक पहली लाइन में ही खारिज कर चुका होता है। त्रुटि आउटपुट को पहले गवाह के रूप में मानें, इसे ध्यान से और पूरी तरह पढ़ें, और तभी तय करें कि क्या जांच करनी है।
समस्या क्षेत्र की बाइनरी खोज द्वारा पृथक्करण करें
कोड को ऊपर से नीचे स्कैन न करें। इसे खोजें। बाइनरी सर्च का उपयोग करें: एक बिंदु खोजें जहाँ स्थिति अभी भी अच्छी है और एक बिंदु जहाँ यह पहले से ख़राब है, फिर मध्यबिंदु जांचें, और दोहराएं, हर बार संदिग्ध क्षेत्र को आधा करते हुए। यह एक हज़ार-लाइन खोज को दस प्रश्नों में बदल देता है। जब रिग्रेशन कमिट की एक श्रृंखला में दिखाई दिया हो, तो बिसेक्शन के साथ इतिहास पर वही विचार लागू करें: git bisect कमिट रेंज में चलता है, और आप हर संशोधन को अच्छा या बुरा चिह्नित करते हैं जब तक यह उस सटीक बदलाव को नाम न दे जिसने दोष को पेश किया। अच्छे-या-बुरे परीक्षण को स्वचालित करें और बिसेक्शन खुद चलता है।
एक न्यूनतम पुनरुत्पादनीय उदाहरण तक कम करें
एक बार जब आप बग को ट्रिगर कर सकें, तो इसे सिकोड़ें। एक न्यूनतम पुनरुत्पादनीय उदाहरण सबसे छोटा इनपुट और कोड पथ है जो अभी भी विफल होता है: डेटा, सुविधाएं, और चरण तब तक हटाएं जब तक कोई और हटाना बग को गायब न कर दे। कमी करना निरर्थक काम नहीं है; आपके द्वारा हटाया गया हर तत्व एक ऐसा कारण है जिसे आपने खारिज कर दिया, इसलिए न्यूनतम केस अक्सर सीधे दोष की ओर इशारा करता है। जब इनपुट बड़ा या संरचित हो, तो डेल्टा डिबगिंग के साथ सिकोड़ने को स्वचालित करें, एक एल्गोरिद्म जो एक असफल इनपुट के टुकड़ों को व्यवस्थित रूप से हटाकर न्यूनतम असफल उपसमुच्चय खोजता है। एक छोटा, स्व-निहित पुनरुत्पादन किसी और टीम को सौंपने के लिए सबसे अच्छी बग रिपोर्ट भी है।
लॉग से इंस्ट्रूमेंट करें, फिर एक इंटरैक्टिव डिबगर का उपयोग करें
टूल को बग से मिलाएं। लॉगिंग और लक्षित इंस्ट्रूमेंटेशन सबसे अच्छे तब होते हैं जब आपको समय के साथ, कई प्रोसेस में, या ऐसे वातावरण में व्यवहार देखने की आवश्यकता हो जिसे आप रोक नहीं सकते। एक इंटरैक्टिव डिबगर, जो आपको ब्रेकपॉइंट सेट करने, लाइन दर लाइन कदम बढ़ाने, और लाइव स्थिति का निरीक्षण करने देता है, सबसे अच्छा तब होता है जब आप कोड स्थानीय रूप से चला सकें और एक एकल निष्पादन को बारीकी से देखना चाहें। इंस्ट्रूमेंटेशन को बिखरे हुए प्रिंट स्टेटमेंट के रूप में नहीं बल्कि एक परिकल्पना से जुड़े जानबूझकर प्रयोग के रूप में जोड़ें, और बग हल होने के बाद इसे हटा दें या इसे स्थायी संरचित लॉगिंग में बढ़ा दें। प्रोडक्शन में, ऑब्ज़र्वेबिलिटी-चालित डिबगिंग पर निर्भर रहें: उच्च-कार्डिनैलिटी घटनाएं और वितरित ट्रेसिंग (अध्याय 9.2) आपको कई सेवाओं में एक रिक्वेस्ट का पीछा करने देती हैं, जो अक्सर एक वितरित सिस्टम को डिबग करने का एकमात्र तरीका है जिससे आप डिबगर संलग्न नहीं कर सकते।
ठीक करने से पहले एक असफल परीक्षण लिखें जो बग को पकड़ता है
फ़िक्स लिखने से पहले, एक परीक्षण लिखें जो बग के कारण विफल होता है। यह एक साथ तीन काम करता है: यह साबित करता है कि आप वास्तव में कारण समझते हैं, यह ठीक से परिभाषित करता है कि “ठीक हुआ” का क्या मतलब है, और यह एक स्थायी रक्षक बन जाता है। फिर फ़िक्स करें और परीक्षण को हरा होते देखें। वह परीक्षण अब आपके सूट में एक रिग्रेशन परीक्षण रक्षक के रूप में शामिल हो जाता है, ताकि वही दोष बिना ध्यान दिए वापस न आ सके। यह प्रथा डिबगिंग को सीधे आपकी परीक्षण रणनीति (अध्याय 2.4) से जोड़ती है: आप जो भी कठिन बग हल करते हैं वह सूट को पहले से मज़बूत छोड़ता है, और एक अस्थिर परीक्षण को रीट्राई एनोटेशन के बजाय वही उपचार मिलता है (अनिश्चितता को पुनरुत्पादित करें, फिर उसके खिलाफ़ रक्षा करें)।
मूल कारण खोजें, और विश्लेषण को दोषरहित रखें
लक्षण को ठीक करना बग को ठीक करना नहीं है। विफलता को उसके सच्चे मूल तक वापस ट्रेस करें, हर परत पर क्यों पूछते हुए जब तक आप उस कारण तक न पहुँच जाएं जिसे आप छुपाने के बजाय हटा सकें। उन दोषों के लिए जो प्रोडक्शन तक पहुंचे, घटना प्रबंधन (अध्याय 9.3) के भाग के रूप में एक दोषरहित मूल-कारण विश्लेषण चलाएं: उन सिस्टम और प्रक्रिया स्थितियों पर ध्यान केंद्रित करें जिन्होंने बग को शिप और जीवित रहने दिया, कभी उस व्यक्ति पर नहीं जिसने वह लाइन लिखी। दोष जानकारी को भूमिगत कर देता है, और डिबगिंग जानकारी पर चलती है। परिणाम एक फ़िक्स और यह बदलाव दोनों है कि अगली बार उस श्रेणी का दोष पहले कैसे पकड़ा जाए।
ट्रेड-ऑफ़: फ़ायदे और नुकसान
| दृष्टिकोण | फ़ायदे | नुकसान |
|---|---|---|
| लॉगिंग और इंस्ट्रूमेंटेशन | प्रोडक्शन और वितरित सिस्टम में काम करता है; समय के साथ व्यवहार पकड़ता है | शोर, लागत, और लॉग फैलाव; टाइमिंग बग को बिगाड़ सकता है |
| इंटरैक्टिव डिबगर | सटीक, लाइव स्थिति निरीक्षण; स्थानीय बग के लिए तेज़ | प्रतिबंधित या एयर-गैप्ड प्रोडक्शन में बेकार; समवर्तता बग छुपा सकता है |
| पहले-पुनरुत्पादन अनुशासन | अटकल को मापन में बदलता है; एक असफल परीक्षण सक्षम करता है | शुरुआत में धीमा; कुछ बग वास्तव में ट्रिगर करना कठिन होते हैं |
| बाइनरी सर्च और बिसेक्शन | अपरिचित कोड में भी तेज़ पृथक्करण | एक विश्वसनीय अच्छे-या-बुरे परीक्षण की आवश्यकता; जब बग परस्पर क्रिया करें तो कठिन |
| डेल्टा-डिबगिंग कमी | विशाल इनपुट को स्वचालित रूप से ट्रिगर तक सिकोड़ता है | सेटअप लागत; मानता है कि विफलता निर्धारक है |
| अभी लक्षण ठीक करें | दबाव में सेवा को तेज़ी से बहाल करता है | मूल कारण को वापस आने देता है; ऋण जमा करता है |
केंद्रीय तनाव गति बनाम निश्चितता है। एक प्रोडक्शन घटना के तहत आपको सेवा बहाल करने के लिए पहले खून बहना रोकना पड़ सकता है (एक रोलबैक या लक्षण पैच), और यह वैध है। गलती वहीं रुक जाना है। तनाव को दो कामों को अलग करके हल करें: उपयोगकर्ताओं की रक्षा के लिए तेज़ी से शमन करें, फिर पुनरुत्पादित करें, मूल कारण खोजें, और दोष को बंद मानने से पहले रिग्रेशन रक्षक जोड़ें। बिना फ़ॉलो-अप के एक लक्षण फ़िक्स एक ऐसा बग है जिससे आप फिर मिलने के लिए सहमत हो गए हैं।
अपनी टीम के साथ चर्चा के लिए प्रश्न
जब कोई कठिन बग से टकराता है, तो वे सबसे पहले क्या करते हैं, और क्या यह पुनरुत्पादन है या अटकल? ईमानदार उत्तर यह बताता है कि क्या आपकी टीम के पास एक साझा तरीका है या निजी लोककथाओं से भरा एक कमरा। लोगों से कहें कि वे अपने पिछले कठिन दोष को ज़ोर से बताएं: क्या उन्हें पहले एक विश्वसनीय पुनरुत्पादन मिला, या उन्होंने कोड बदलना और चीज़ें पुनः आरंभ करना शुरू कर दिया? एक टीम जो पहले पुनरुत्पादित करती है वह एक बग को लोगों के बीच सौंप सकती है, क्योंकि पुनरुत्पादन यात्रा करता है; एक टीम जो अटकल लगाती है नहीं कर सकती, क्योंकि हर प्रयास अदोहराने योग्य है। यह टीम के बढ़ने के साथ और मायने रखता है, क्योंकि जो व्यक्ति लक्षण देखता है वह तेज़ी से वह व्यक्ति नहीं रहता जो इसे ठीक कर सकता है। यदि डिफ़ॉल्ट अटकल है, तो पहले-पुनरुत्पादन को एक मानदंड के रूप में सहमत करें और एक साफ़ पुनरुत्पादन को बग टिकट में प्रवेश की कीमत बनाएं।
क्या हमारे ठीक किए गए बग वापस आते हैं, और यदि आते तो क्या हमें पता चलेगा? एक दोष जो वापस आता है वह एक दोष है जिसका मूल कारण कभी हटाया नहीं गया और जिसका फ़िक्स कभी किसी परीक्षण द्वारा सुरक्षित नहीं किया गया। अपनी पिछली तिमाही की घटनाओं और फिर से खोले गए टिकटों को निकालें और गिनें कि कितने पहले के बग की पुनरावृत्ति या करीबी चचेरे भाई थे। हर पुनरावृत्ति इस बात का सबूत है कि टीम ने एक लक्षण को पैच किया, असफल परीक्षण छोड़ दिया, या मूल-कारण विश्लेषण बहुत जल्दी रोक दिया। समाधान एक नियम है: कोई बग तब तक बंद नहीं है जब तक कोई परीक्षण जो पुराने व्यवहार पर विफल होता है वह नए पर पास न हो जाए और सूट में शामिल न हो जाए। एक हाल की आवर्ती बग लाएं और पूछें कि कौन सा रक्षक इसे पकड़ता, क्योंकि वही रक्षक है जो आपके पास नहीं था।
क्या हम अपने प्रोडक्शन सिस्टम को बिल्कुल डिबग कर सकते हैं, यह देखते हुए कि हमें उन्हें छूने की कितनी अनुमति है? एंटरप्राइज़ और विशेष रूप से सरकारी वातावरणों में, आप अक्सर डिबगर संलग्न नहीं कर सकते, वास्तविक डेटा से पुनरुत्पादित नहीं कर सकते, और ऑडिट ट्रेल के बिना एक चलते हुए सिस्टम को बदल नहीं सकते। यदि आपकी एकमात्र डिबगिंग तकनीक एक स्थानीय इंटरैक्टिव डिबगर है, तो आप ठीक वहीं अंधे हैं जहाँ सबसे कठिन बग रहते हैं। पूछें कि एक प्रोडक्शन विफलता वास्तव में क्या सबूत पीछे छोड़ती है: संरचित लॉग, वितरित ट्रेस (अध्याय 9.2), कोर डंप, या प्रतिकृत निर्माण आर्टिफ़ैक्ट। अभी तय करें कि आपको डिफ़ॉल्ट रूप से क्या पकड़ना चाहिए ताकि भविष्य की घटना निदान योग्य हो, क्योंकि आप उस विफलता में इंस्ट्रूमेंटेशन नहीं जोड़ सकते जो पहले ही हो चुकी है। विनियमित सेटिंग्स में, पुष्टि करें कि वही ट्रेल आपकी ऑडिट बाध्यताओं को भी संतुष्ट करता है।
जब एक प्रोडक्शन घटना हमें तेज़ी से खून बहना रोकने पर मजबूर करती है, तो हम कैसे सुनिश्चित करें कि मूल कारण बाद में भी मिल जाए? एक घटना के तहत, एक रोलबैक या एक लक्षण पैच उपयोगकर्ताओं की रक्षा के लिए सही पहला कदम है, लेकिन खतरा यह है कि सेवा लौटते ही टिकट बंद हो जाता है और अंतर्निहित दोष का कभी निदान नहीं होता। एक बड़ी टीम के लिए यही वह जगह है जहाँ ऋण अदृश्य रूप से जमा होता है, क्योंकि उसी श्रेणी की विफलता महीनों बाद एक अलग सेवा और एक अलग ऑन-कॉल इंजीनियर पर फिर से उभरती है। अपनी पिछली कुछ सेवेरिटी-वन घटनाओं को लाएं और हर एक को जांचें: क्या शमन के बाद एक पुनरुत्पादन, एक मूल-कारण विश्लेषण, और एक रिग्रेशन रक्षक आया, या क्या कहानी “सेवा बहाल” पर समाप्त हुई? एक स्पष्ट नियम पर सहमत हों कि एक शमन की गई घटना तब तक खुली रहती है जब तक मूल कारण समझा और सुरक्षित न हो, और नाम दें कि इस फ़ॉलो-थ्रू का मालिक कौन है। एंटरप्राइज़ और सरकारी सेटिंग्स में, इसे अपनी घटना-प्रबंधन प्रक्रिया (अध्याय 9.3) से जोड़ें ताकि घटना-पश्चात समीक्षा एक आवश्यक, ऑडिट योग्य कदम हो, न कि एक शिष्टाचार जो अगली आग शुरू होने पर फिसल जाए।
किसी विफलता का कितना हिस्सा हम वास्तव में बाद में पुनर्निर्मित कर सकते हैं, और यह किसने तय किया कि हम डिफ़ॉल्ट रूप से क्या पकड़ते हैं? आप उस विफलता में इंस्ट्रूमेंटेशन नहीं जोड़ सकते जो पहले ही हो चुकी है, इसलिए किसी भी घटना की निदान योग्यता उन लॉग, ट्रेस, मेट्रिक, और डंप द्वारा पहले से तय होती है जिन्हें आपने उत्सर्जित करने के लिए चुना। प्रतिस्पर्धी विचार लागत और शोर है: उच्च-कार्डिनैलिटी घटनाएं और पूर्ण ट्रेसिंग मुफ़्त नहीं हैं, और अत्यधिक लॉगिंग संकेत को दबा देती है जबकि स्टोरेज और, विनियमित संदर्भों में, आपके डेटा-प्रतिधारण जोखिम को बढ़ा देती है। एक वास्तविक हालिया घटना लाएं और पूछें कि उसने क्या सबूत पीछे छोड़ा, फिर पीछे की ओर काम करें कि आप क्या पकड़ना चाहते और उसे रखने की लागत क्या होती। जानबूझकर तय करें कि कौन से सिग्नल डिफ़ॉल्ट रूप से चालू हैं बनाम सैंपल किए गए या ऑप्ट-इन, और उस निर्णय को रिकॉर्ड करें ताकि यह एक नीति हो, दुर्घटना नहीं। किसी एंटरप्राइज़ या सरकारी सिस्टम के लिए, जोड़ें कि उस ऑब्ज़र्वेबिलिटी बजट के लिए कौन जवाबदेह है और क्या पकड़ा गया ट्रेल ऑडिट, गोपनीयता, और डेटा-निवास बाध्यताओं को भी संतुष्ट करता है।
क्या हम डिबगिंग को एक सिखाए गए, मापने योग्य कौशल के रूप में मानते हैं, या नए इंजीनियर इसे परासरण द्वारा आत्मसात करते हैं? डिबगिंग सीखने योग्य है, फिर भी अधिकांश टीमें इसे कभी स्पष्ट रूप से नहीं सिखातीं, इसलिए जूनियर जो भी लोककथा उनके सबसे निकट बैठी है उसे विरासत में पाते हैं, और पहले-पुनरुत्पादन विधि असमान रूप से फैलती है या बिल्कुल नहीं। तनाव यह है कि जानबूझकर सिखाना (कठिन बग पर पेयरिंग, घटना-पश्चात निष्कर्ष लिखना, मेट्रिक ट्रैक करना) वरिष्ठ समय की लागत लेता है जो हमेशा कहीं और आवश्यक महसूस होता है। चर्चा में दो संख्याएं लाएं: आपकी पुनरावृत्ति-दोष दर और आपका निदान-समय, क्योंकि यदि आप उन्हें माप नहीं सकते तो आप यह नहीं बता सकते कि आपकी विधि सुधर रही है या क्षीण हो रही है। विचार करें कि क्या ऑनबोर्डिंग में एक वास्तविक डिबगिंग अभ्यास शामिल है और क्या मूल-कारण निष्कर्ष वास्तव में पहले की खोज को फ़ीड करते हैं। एक बड़े या सार्वजनिक संगठन में, एक दस्तावेज़ीकृत, मापी गई डिबगिंग प्रथा इंजीनियरिंग कठोरता के सबूत के रूप में भी काम आती है जिसे ऑडिटर, नियामक, और निगरानी निकाय तेज़ी से देखने की अपेक्षा करते हैं।
क्षेत्र लेंस
स्टार्टअप। मुट्ठी भर इंजीनियरों और बिना किसी अतिरिक्त क्षमता के साथ, आपका लक्ष्य बग को सस्ते में पुनरुत्पादित करने योग्य और भूलना असंभव बनाना है, भारी प्रक्रिया बनाना नहीं। git bisect, एक तेज़ स्थानीय पुनरुत्पादन, और हर ठीक किए गए बग के लिए एक असफल परीक्षण पर निर्भर रहें, क्योंकि यह आदत मिनट खर्च करती है और आपको शिप करने की कोशिश करते समय उसी दोष के लिए फिर से भुगतान करने से रोकती है। औपचारिक पोस्टमॉर्टम छोड़ें, लेकिन रिग्रेशन परीक्षण कभी न छोड़ें: यह एक ऐसा आर्टिफ़ैक्ट है जो हमेशा वहन करने के लिए काफ़ी छोटा और हमेशा रखने के लिए काफ़ी मूल्यवान है।
लघु व्यवसाय। आपके पास संभवतः कोई समर्पित रिलायबिलिटी या ऑब्ज़र्वेबिलिटी विशेषज्ञ और एक तंग टूलिंग बजट नहीं है, इसलिए जो आपका स्टैक पहले से देता है उसे प्राथमिकता दें: पठनीय स्टैक ट्रेस, संरचित लॉग, और उन फ़्रेमवर्क व होस्टेड सेवाओं में निर्मित ट्रेसिंग जो आपने खरीदी हैं। जब आप एक नए प्लेटफ़ॉर्म का मूल्यांकन करें, तो तौलें कि यह विफलताओं को कितना निदान योग्य बनाता है, क्योंकि एक सस्ता टूल जो यह छुपाता है कि क्या ग़लत हुआ वह आपको लाइसेंस पर बचाए गए पैसे से कहीं अधिक अटकल के समय में खर्च कराता है। पहले-पुनरुत्पादन और एक-बार-में-एक-बदलाव मुफ़्त अनुशासन हैं जो तब सबसे तेज़ी से भुगतान करते हैं जब किसी के पास बख्शने के लिए घंटे नहीं होते।
एंटरप्राइज़। आपके कठिन बग सेवा और टीम सीमाओं को पार करते हैं, इसलिए जो व्यक्ति लक्षण देखता है वह शायद ही कारण का मालिक होता है, और एक साझा तरीका किसी भी व्यक्ति के कौशल से अधिक मायने रखता है। टीमों में पहले-पुनरुत्पादन, बाइनरी-सर्च पृथक्करण, फ़िक्स से पहले एक असफल परीक्षण, और दोषरहित पोस्टमॉर्टम को मानकीकृत करें, और वितरित ट्रेसिंग (अध्याय 9.2) में निवेश करें ताकि एक रिक्वेस्ट का सेवाओं में पीछा किया जा सके। डिबगिंग को एक मापी गई क्षमता के रूप में प्रबंधित करें: पुनरावृत्ति-दोष दर और निदान-समय को ट्रैक करें, और मूल-कारण निष्कर्षों को पहले की खोज में वापस फ़ीड करें ताकि उसी श्रेणी की विफलता आपके सेवा मानचित्र का दौरा न करे।
सरकार। खरीद नियम, प्रतिबंधित वातावरण, और सार्वजनिक जवाबदेही आकार देते हैं कि आप बिल्कुल कैसे डिबग कर सकते हैं। आप अक्सर प्रोडक्शन में डिबगर संलग्न नहीं कर सकते या नागरिक डेटा को लैपटॉप पर कॉपी नहीं कर सकते, इसलिए जो अनुमति है उससे निदान के लिए डिज़ाइन करें: प्रतिकृत निर्माण, एक पृथक एन्क्लेव में सिंथेटिक रिकॉर्ड, और डिफ़ॉल्ट रूप से पकड़े गए संरचित लॉग व ट्रेस। हर निदान चरण और हर बदलाव को ऑडिट ट्रेल में रिकॉर्ड करें, और विक्रेताओं से यह आवश्यक करें कि वे पर्याप्त टेलीमेट्री और निर्माण प्रतिकृतता उजागर करें ताकि आप आपूर्तिकर्ता की बात पर निर्भर रहने के बजाय स्वतंत्र रूप से विफलताओं की जांच कर सकें।
उदाहरण
स्टार्टअप। एक चार-इंजीनियर टीम देखती रहती है कि चेकआउट कुछ उपयोगकर्ताओं के लिए विफल होता है, लेकिन परीक्षण में कभी नहीं। अटकल लगाने के बजाय, एक इंजीनियर सटीक विफल रिक्वेस्ट पेलोड को दोहराकर एक विश्वसनीय पुनरुत्पादन पकड़ता है, फिर वह स्टैक ट्रेस पढ़ता है जिसे वे नज़रअंदाज़ कर रहे थे, जो एक तारीख-पार्सिंग कॉल की ओर इशारा करता है। सप्ताह के कमिट पर एक त्वरित git bisect उस बदलाव को नाम देता है जिसने एक तारीख लाइब्रेरी को बदल दिया था। वे अपराधी टाइमस्टैम्प के साथ एक असफल परीक्षण लिखते हैं, पार्सर ठीक करते हैं, परीक्षण को हरा होते देखते हैं, और इसे सूट में रखते हैं। पूरी जांच एक दोपहर लेती है क्योंकि उन्होंने सिद्धांत बनाने से पहले पुनरुत्पादित किया, और बग कभी वापस नहीं आता।
एंटरप्राइज़। एक भुगतान प्लेटफ़ॉर्म रुक-रुक कर टाइमआउट देखता है जिसे कोई एक टीम समझा नहीं सकती, क्योंकि लक्षण चेकआउट में दिखाई देता है लेकिन कारण तीन सेवाएं दूर रहता है। ऑन-कॉल इंजीनियर सेवा सीमाओं में एक असफल रिक्वेस्ट का पीछा करने के लिए वितरित ट्रेसिंग (अध्याय 9.2) का उपयोग करते हैं और एक डाउनस्ट्रीम कॉल खोजते हैं जो कभी-कभी समवर्ती लोड के तहत डेडलॉक हो जाती है, एक क्लासिक रेस कंडीशन जहाँ परिणाम थ्रेड्स के बीच बदकिस्मत समय पर निर्भर करता है। वे इसे एक लोड टेस्ट से पुनरुत्पादित करते हैं, इसे एक असफल इंटीग्रेशन टेस्ट में पकड़ते हैं, लॉकिंग ठीक करते हैं, और एक दोषरहित पोस्टमॉर्टम (अध्याय 9.3) चलाते हैं जो एक ट्रेसिंग स्पैन और एक अलर्ट जोड़ता है ताकि अगली घटना दिनों में नहीं, मिनटों में पकड़ी जाए।
सरकार। एक लाभ एजेंसी अपनी केस सिस्टम को एक एयर-गैप्ड वातावरण में चलाती है जहाँ इंजीनियर प्रोडक्शन में डिबगर संलग्न नहीं कर सकते और नागरिक डेटा को अपने लैपटॉप पर कॉपी नहीं कर सकते। एक गणना दोष सामंजस्य में सामने आता है। टीम वहाँ से डिबग करती है जो वातावरण अनुमति देता है: संरचित लॉग, एक प्रतिकृत निर्माण जिसे वे एक पृथक परीक्षण एन्क्लेव में खड़ा कर सकते हैं, और सिंथेटिक रिकॉर्ड जो विफल केस को फिर से बनाते हैं। हर निदान चरण ऑडिट ट्रेल में दर्ज होता है, फ़िक्स सबूत के रूप में एक असफल-फिर-सफल परीक्षण के साथ शिप होता है, और मूल-कारण विश्लेषण एक नई प्री-रिलीज़ जांच को फ़ीड करता है। क्योंकि पुनरुत्पादन ने सिंथेटिक डेटा का उपयोग किया, कोई नागरिक रिकॉर्ड कभी सीमा से बाहर नहीं गया।
बिज़नेस केस: प्रेरणाएं, ROI, और TCO
अनुशासित डिबगिंग पर रिटर्न इंजीनियर घंटों में मापा जाता है जो अटकल में खर्च नहीं होते और उन दोषों में जो पुनरावृत्त नहीं होते। एक अनिदानित रुक-रुक कर आने वाला बग वरिष्ठ समय के दिन और बार-बार ऑन-कॉल एस्केलेशन खपा सकता है; एक पहले-पुनरुत्पादन विधि इसे एक सीमित, प्रत्यायोजन योग्य कार्य में बदल देती है, और असफल-परीक्षण की आदत उसी दोष को अगली तिमाही में फिर से बिल भेजने से रोकती है। एक बड़े संगठन में, उसी बग के लिए कभी फिर से भुगतान न करने का यौगिक प्रभाव महत्वपूर्ण है, और यह सीधे उस परिवर्तन-विफलता दर और औसत पुनर्प्राप्ति समय को सुधारता है जिसे नेतृत्व पहले से ट्रैक करता है।
स्वामित्व की कुल लागत अधिकतर प्रशिक्षण और टूलिंग है, और यह मामूली है। आपको साझा परंपराओं (पहले पुनरुत्पादन, एक बार में एक बदलाव, फ़िक्स से पहले एक असफल परीक्षण), टूलचेन में पहले से आम डिबगर और ट्रेसिंग, और अध्याय 9.2 में वर्णित ऑब्ज़र्वेबिलिटी निवेश की आवश्यकता है। बड़ी, छुपी हुई लागत विकल्प है: अंधविश्वास की एक संस्कृति जहाँ इंजीनियर शॉटगन बदलाव लागू करते हैं, लक्षण पैच किए जाते हैं और वापस आते हैं, और ऑन-कॉल भार असीमित रूप से बढ़ता है। अकेले ऑन-कॉल थकान को कम करना अक्सर निवेश को उचित ठहराता है, और नेतृत्व के लिए केस को सबसे सरलता से कम पुनरावृत्त घटनाओं और आदतों व इंस्ट्रूमेंटेशन में एक बार की लागत के लिए तेज़ रिकवरी के रूप में बताया जा सकता है।
एंटी-पैटर्न और गड्ढे
- शॉटगन डिबगिंग: एक साथ कई चीज़ें बदलना, ताकि एक फ़िक्स भी आपको कारण के बारे में कुछ न सिखाए।
- बिना पुनरुत्पादन के ठीक करना: एक ऐसे बग पर जीत घोषित करना जिसे आप कभी मांग पर ट्रिगर नहीं कर पाए।
- त्रुटि आउटपुट को नज़रअंदाज़ करना: उन कारणों पर सिद्धांत बनाना जिन्हें स्टैक ट्रेस पहले ही खारिज कर चुका था।
- लक्षण पैचिंग: लक्षण को चुप कराना जबकि मूल कारण वापस आने के लिए जीवित रहता है।
- प्रिंट-स्टेटमेंट फैलाव: कोड में छोड़ा गया बिखरा हुआ डिबग आउटपुट, एक परिकल्पना से जुड़े प्रयोग के बजाय शोर जोड़ना।
- रिग्रेशन परीक्षण छोड़ना: बग को ठीक करना लेकिन कोई रक्षक न छोड़ना, ताकि यह चुपचाप वापस आ सके।
- अस्थिर परीक्षणों को फिर से चलाना: अंतर्निहित रेस कंडीशन या हाइज़नबग को डिबग करने के बजाय रीट्राई के साथ अनिश्चितता छुपाना, एक बग जो आपके इसे देखने की कोशिश करते ही बदल जाता है या गायब हो जाता है।
- दोष-चालित पोस्टमॉर्टम: लेखक को दंडित करना, जो उस जानकारी को भूमिगत कर देता है जिस पर डिबगिंग निर्भर करती है।
परिपक्वता मॉडल
- स्तर 1, आरंभ: डिबगिंग व्यक्तिगत लोककथा और प्रतिक्रिया है। इंजीनियर अटकल लगाते हैं, शॉटगन बदलाव लागू करते हैं, और चीज़ें पुनः आरंभ करते हैं। बग को लक्षण पर ठीक किया जाता है, पुनरुत्पादन दुर्लभ हैं, और वही दोष पुनरावृत्त होते हैं। प्रोडक्शन मुश्किल से निदान योग्य है, और कोई भी बग को किसी और को नहीं सौंप सकता क्योंकि कोई प्रयास दोहराने योग्य नहीं है।
- स्तर 2, विकास: कुछ इंजीनियर विश्वसनीय रूप से पुनरुत्पादित करते हैं, स्टैक ट्रेस पढ़ते हैं, और डिबगर का उपयोग करते हैं, लेकिन प्रथा असंगत है और व्यक्ति से व्यक्ति और टीम से टीम में भिन्न होती है। लॉगिंग मौजूद है लेकिन शोरगुल भरी और असंरचित है। फ़िक्स कभी-कभी एक असफल परीक्षण के साथ शिप होते हैं, अक्सर नहीं, और मूल-कारण विश्लेषण तभी होता है जब कोई ज़ोर देता है।
- स्तर 3, मानकीकरण: पहले-पुनरुत्पादन, बाइनरी-सर्च पृथक्करण, एक बार में एक बदलाव, और फ़िक्स से पहले एक असफल परीक्षण संगठन भर में लागू दस्तावेज़ीकृत टीम मानदंड हैं। बिसेक्शन और डेल्टा-डिबगिंग कमी सामान्य प्रथा है। प्रोडक्शन में संरचित लॉगिंग और ट्रेसिंग (अध्याय 9.2) है, और दोषरहित पोस्टमॉर्टम (अध्याय 9.3) हर बचे हुए दोष के लिए मानक प्रतिक्रिया है।
- स्तर 4, प्रबंधन: डिबगिंग प्रथा को बेसलाइन के विरुद्ध मापा और नियंत्रित किया जाता है। पुनरावृत्ति-दोष दर, निदान-समय, फिर से खोले गए टिकट की संख्या, और रिग्रेशन परीक्षण के साथ शिप हुए फ़िक्स के हिस्से को प्रति टीम ट्रैक किया जाता है और एक कैडेंस पर समीक्षा की जाती है। पुनरुत्पादन और मूल-कारण पूर्णता को अच्छे इरादों के बजाय गेट के रूप में माना जाता है, और बेसलाइन के विरुद्ध रुझान यह तय करते हैं कि आप टूलिंग, प्रशिक्षण, और ऑब्ज़र्वेबिलिटी में कहाँ निवेश करते हैं।
- स्तर 5, आर्केस्ट्रेशन: डिबगिंग एक सिखाया गया कौशल है जो गुणवत्ता (अध्याय 2.11) और घटना प्रबंधन (अध्याय 9.3) के साथ एकीकृत है, और पूरा लूप लगातार अनुकूलित होता है। ऑब्ज़र्वेबिलिटी इस तरह डिज़ाइन की गई है कि अधिकांश प्रोडक्शन बग बिना डिबगर के निदान योग्य हों, हर हल किया गया बग रिग्रेशन सूट को मज़बूत करता है, और मूल-कारण निष्कर्ष पहले की खोज को फ़ीड करते हैं ताकि दोषों की श्रेणियां फिर से निदान होने के बजाय रोकी जाएं। संगठन अपने सिस्टम और विफलता मोड के विकसित होने पर प्रयास को पुनर्संतुलित करता है, और पुनरावृत्ति-दोष दर लगातार गिरती रहती है।
चर्चा के लिए विचार
- आपके हाल के बग का कितना हिस्सा किसी के कोड बदलने से पहले विश्वसनीय रूप से पुनरुत्पादित किया गया था, और वह हिस्सा आपकी विधि के बारे में क्या बताता है?
- जब एक रिग्रेशन दिखाई देता है, तो क्या आपकी टीम बिसेक्शन का सहारा लेती है, या कोई इसे पहचानने तक हाथ से कोड पढ़ती है?
- आज आपका प्रोडक्शन सिस्टम कितना निदान योग्य है, और पहले ही हो चुकी विफलता के बारे में पकड़ने के लिए आप क्या देते?
- क्या आपके फ़िक्स लगातार एक असफल-फिर-सफल परीक्षण के साथ शिप होते हैं, और यदि नहीं, तो वह अनुशासन कहाँ टूटता है?
- आप अस्थिर परीक्षणों को कैसे संभालते हैं: अनिश्चितता को डिबग करते हैं, या रीट्राई से उस पर पर्दा डालते हैं?
- क्या डिबगिंग नए इंजीनियरों को जानबूझकर सिखाई जाती है, या उन्हें परासरण द्वारा लोककथा आत्मसात करने के लिए छोड़ दिया जाता है?
मुख्य निष्कर्ष
- डिबगिंग परिकल्पना परीक्षण है: विश्वसनीय रूप से पुनरुत्पादित करें, त्रुटि और स्टैक ट्रेस पढ़ें, फिर स्कैनिंग के बजाय बाइनरी सर्च और बिसेक्शन द्वारा पृथक करें।
- विफलता को एक न्यूनतम पुनरुत्पादनीय उदाहरण तक कम करें, बड़े इनपुट के लिए डेल्टा डिबगिंग का उपयोग करते हुए, क्योंकि हटाया गया हर तत्व एक खारिज किया गया कारण है।
- टूल को बग से मिलाएं: प्रोडक्शन और वितरित सिस्टम के लिए इंस्ट्रूमेंटेशन और ट्रेसिंग (अध्याय 9.2), स्थानीय जांच के लिए इंटरैक्टिव डिबगर।
- ठीक करने से पहले एक असफल परीक्षण लिखें जो बग को पकड़ता है, ताकि फ़िक्स साबित हो और दोष हमेशा के लिए सुरक्षित रहे (अध्याय 2.4)।
- मूल कारण खोजें और हटाएं, दोषरहित पोस्टमॉर्टम चलाएं (अध्याय 9.3), और डिबगिंग को एक सीखने योग्य कौशल मानें, लोककथा नहीं।
- एक बार में एक चीज़ बदलें; शॉटगन बदलाव और लक्षण पैच सबूत नष्ट करते हैं और बग को वापस आने के लिए आमंत्रित करते हैं।
संदर्भ और आगे पढ़ना
- David J. Agans, Debugging: The 9 Indispensable Rules for Finding Even the Most Elusive Software and Hardware Problems
- Andreas Zeller, Why Programs Fail: A Guide to Systematic Debugging
- Andreas Zeller and Ralf Hildebrandt, “Simplifying and Isolating Failure-Inducing Input” (the delta debugging algorithm)
- Brian W. Kernighan and Rob Pike, The Practice of Programming (chapter on debugging)
- Andrew Hunt and David Thomas, The Pragmatic Programmer (the chapters on debugging and assertions)
- Steve McConnell, Code Complete: A Practical Handbook of Software Construction (the debugging chapter)
- John Regehr, “Reducers Are Fuzzers” and related writing on test-case reduction
- Charity Majors, Liz Fong-Jones, and George Miranda, Observability Engineering (debugging production with high-cardinality telemetry and tracing)
- Betsy Beyer, Chris Jones, Jennifer Petoff, and Niall Richard Murphy, eds., Site Reliability Engineering (blameless postmortems and production debugging)