फ्रंट-एंड एंट्रॉपी (Front-end entropy) वास्तविक है। एक कोडबेस रातों-रात नहीं ढहता। यह धीरे-धीरे जमा होता जाता है। किसी मंगलवार को आप एक date-formatting library जोड़ते हैं। छह महीने बाद कोई और जोड़ देता है क्योंकि उसे पहली वाली नहीं मिली। उन ब्राउज़रों के लिए polyfills जमा होते रहते हैं जिन्हें आप अब सपोर्ट नहीं करते। बिल्ड टूल्स एक-दूसरे के ऊपर परत दर परत चढ़ते जाते हैं। अंततः, node_modules फ़ोल्डर एक डिजिटल कबाड़खाना (junk drawer) बन जाता है जहाँ डर के बिना कुछ भी फेंका नहीं जा सकता। आप अपडेट करना बंद कर देते हैं। फिर आप देखना भी बंद कर देते हैं। तभी हर छोटा बदलाव एक जुए में बदल जाता है।
एक पुराने प्रोजेक्ट में Material UI को अपडेट करने की कोशिश करते समय मुझे इस समस्या का सामना करना पड़ा। मैंने package.json खोला और मुश्किल से आधे entries को पहचान पाया। दर्जनों libraries वहाँ पड़ी थीं, कुछ सालों से आउटडेटेड थीं, तो कुछ इतनी अज्ञात (obscure) थीं कि मुझे यह पता लगाने के लिए git blame देखना पड़ा कि उन्हें किसने और क्यों जोड़ा था। मैंने नए Material UI वर्शन के लिए install कमांड चलाई और टर्मिनल peer dependency warnings से भर गया। जिस पैकेज को मैं अपडेट करना चाहता था वह ठीक था। लेकिन उसके आसपास का ecosystem ठीक नहीं था। मुझे एहसास हुआ कि मैं कोई अपग्रेड नहीं कर रहा था। मैं तो किसी खंडहर की खुदाई कर रहा था।
यह अव्यवस्था गर्व से कहीं अधिक महंगी क्यों पड़ती है
डिपेंडेंसीज़ (dependencies) को नज़रअंदाज़ करना केवल दिखावे की समस्या नहीं है। यह वास्तविक और महंगी समस्याएँ पैदा करता है।
Security risks स्पष्ट खतरा हैं। छोड़े गए (abandoned) पैकेजों में ऐसी vulnerabilities होती हैं जिन्हें स्कैनर्स हर हफ्ते फ्लैग करते हैं। इससे भी बुरा यह है कि जो libraries आपने सीधे इंस्टॉल की हैं वे ठीक हो सकती हैं, लेकिन उनके द्वारा खींची गई transitive dependencies ठीक नहीं हो सकतीं। आप अनजाने में किसी और का technical debt विरासत में ले लेते हैं।
लागत समय के साथ बढ़ती जाती है। आप जितना अधिक इंतज़ार करेंगे, वर्शन का अंतर उतना ही बड़ा होता जाएगा। React के एक major वर्शन का जंप करना एक काम है। लेकिन तीन वर्शन का जंप एक ऐसा migration project है जो हफ्तों खा सकता है। आपको bug fixes, performance improvements और आधुनिक टूल्स के साथ compatibility मिलना बंद हो जाती है। टीम अंततः उन सीमाओं के इर्द-गिर्द काम करने लगती है जिनका अब कोई अस्तित्व ही नहीं है।
Libraries खत्म हो जाती हैं। बिना किसी सक्रिय maintainer वाला पैकेज डिफ़ॉल्ट रूप से आपका निजी fork बन जाता है। जब यह टूटता है, तो आधी रात को इसका minified source code पढ़ने वाला आप ही होते हैं। कम्युनिटी बेहतर समाधानों की ओर बढ़ चुकी होती है, और आपकी टीम एक 'भूत' (ghost) को बनाए रखने में फंसी रहती है।
काम की गति (Velocity) गिर जाती है। नए डेवलपर्स अपने शुरुआती दिन उन अजीबोगरीब (idiosyncratic) APIs को सीखने में बिताते हैं जो वेब स्टैंडर्ड्स या मुख्यधारा के विकल्पों द्वारा प्रतिस्थापित (superseded) किए जा चुके हैं। फीचर्स शिप करने के बजाय, आपके सीनियर इंजीनियर्स इतिहासकार बन जाते हैं, जो यह समझाते हैं कि यह प्रोजेक्ट अभी भी 2015 के task runner का उपयोग क्यों कर रहा है।
किसी भी वर्शन को छूने से पहले ऑडिट करें
सबसे बड़ी गलती एक blanket update चलाना और यह उम्मीद करना है कि टेस्ट पास हो जाएंगे। एक ऑडिट से शुरुआत करें। package.json उठाएं और हर entry से सवाल करें।
चार सवाल पूछें:
- यह किस समस्या का समाधान करता है?
- हम इसका उपयोग ठीक कहाँ करते हैं?
- क्या यह अभी भी आवश्यक है?
- क्या अब कोई बेहतर विकल्प मौजूद है?
आपको redundancy (अनावश्यक दोहराव) मिलेगी। हो सकता है कि moment और date-fns दोनों लिस्ट में हों क्योंकि दो डेवलपर्स ने अलग-अलग समय पर एक ही समस्या को हल किया था। हो सकता है कि Internet Explorer के लिए एक polyfill अभी भी शामिल हो, भले ही आपके analytics दिखा रहे हों कि legacy browsers से ज़ीरो ट्रैफिक आ रहा है। शायद fetch के चारों ओर एक custom wrapper को हटाया जा सकता है क्योंकि आधुनिक ब्राउज़र edge cases को natively संभालते हैं।
कभी-कभी अपडेट करने से बेहतर उसे बदलना होता है। तीन साल के breaking changes के बीच एक छोड़ी गई charting library से जूझने में एक स्थिर विकल्प (stable alternative) को अपनाने और कुछ components को फिर से बनाने से ज़्यादा समय लग सकता है। घटाने (subtract करने) के लिए तैयार रहें।
छिपा हुआ स्तर: Transitive Dependencies और Semver
Direct dependencies हिमशैल (iceberg) का केवल दृश्य भाग हैं। असली भार नीचे transitive dependencies में होता है, यानी वे पैकेज जिनकी आपके पैकेज को आवश्यकता होती है। आपने उन्हें नहीं चुना, लेकिन वे आपके बिल्ड में चलते हैं। वे आपके bundle को फुला देते हैं, attack surface को बढ़ाते हैं, और कभी-कभी एक-दूसरे के साथ इस तरह संघर्ष करते हैं जिससे रहस्यमयी (cryptic) build errors पैदा होते हैं।
आपको semantic versioning को उसके वास्तविक अर्थ के लिए पढ़ना चाहिए, न कि उस अर्थ के लिए जिसकी आप आशा करते हैं।
- Major updates: ये migrations हैं। जब तक अन्यथा सिद्ध न हो जाए, इन्हें breaking changes के रूप में मानें। changelog पढ़ें, समय आवंटित करें, और पूरी तरह से परीक्षण करें।
- Minor updates: ये फीचर्स जोड़ते हैं। ये सूक्ष्म तरीकों से व्यवहार को भी बदल सकते हैं। यह न मानें कि ये बिना किसी जोखिम के हैं।
- Patch updates: ये bugs को ठीक करते हैं। ये आमतौर पर सुरक्षित होते हैं, लेकिन यदि आपका कोड उस bug पर निर्भर है, या यदि patch उस internal चीज़ को बदल देता है जिसे आप monkey-patch कर रहे थे, तो भी चीज़ें टूट सकती हैं।
नियमों को जानने से आपको किसी भी चीज़ को छूने से पहले जोखिम को वर्गीकृत करने में मदद मिलती है।
अपने टूल्स का उपयोग एक शिल्पकार की तरह करें
यदि आप Yarn का उपयोग करते हैं, तो कई built-in commands अंदाज़े लगाने की प्रक्रिया को एक व्यवस्थित प्रक्रिया में बदल देते हैं।
सबसे पहले yarn outdated चलाएँ। यह आपको एक snapshot देता है कि क्या drift हो गया है
