डिपेंडेंसी मैनेजमेंट

पिछले पेजों को देखने पर, एक थीम बार-बार दोहराई जाती है: अपना कोड मैनेज करना काफ़ी आसान है, लेकिन उसकी डिपेंडेंसी मैनेज करना ज़्यादा मुश्किल है. डिपेंडेंसी कई तरह की होती हैं: कभी-कभी किसी टास्क पर डिपेंडेंसी होती है. जैसे, “रिलीज़ को पूरा के तौर पर मार्क करने से पहले, दस्तावेज़ पुश करें”. वहीं, कभी-कभी किसी आर्टफ़ैक्ट पर डिपेंडेंसी होती है. जैसे, “मुझे अपना कोड बनाने के लिए, कंप्यूटर विज़न लाइब्रेरी का सबसे नया वर्शन चाहिए”. कभी-कभी आपके कोडबेस के किसी दूसरे हिस्से पर इंटरनल डिपेंडेंसी होती है. वहीं, कभी-कभी किसी दूसरी टीम (आपके संगठन या तीसरे पक्ष) के मालिकाना हक वाले कोड या डेटा पर एक्सटर्नल डिपेंडेंसी होती है. हालांकि, किसी भी मामले में, “मुझे यह चाहिए, तभी मुझे यह मिल सकता है” का आइडिया, बिल्ड सिस्टम के डिज़ाइन में बार-बार दोहराया जाता है. साथ ही, डिपेंडेंसी मैनेज करना, शायद बिल्ड सिस्टम का सबसे बुनियादी काम है.

मॉड्यूल और डिपेंडेंसी मैनेज करना

Bazel जैसे आर्टफ़ैक्ट पर आधारित बिल्ड सिस्टम का इस्तेमाल करने वाले प्रोजेक्ट को मॉड्यूल के सेट में बांटा जाता है. इनमें मॉड्यूल, BUILD फ़ाइलों के ज़रिए एक-दूसरे पर डिपेंडेंसी दिखाते हैं. इन मॉड्यूल और डिपेंडेंसी को सही तरीके से व्यवस्थित करने से, बिल्ड सिस्टम की परफ़ॉर्मेंस और उसे बनाए रखने में लगने वाले काम, दोनों पर काफ़ी असर पड़ सकता है.

फ़ाइन-ग्रेन्ड मॉड्यूल और 1:1:1 नियम का इस्तेमाल करना

आर्टफ़ैक्ट पर आधारित बिल्ड को स्ट्रक्चर करते समय, पहला सवाल यह होता है कि किसी एक मॉड्यूल में कितनी फ़ंक्शनैलिटी शामिल होनी चाहिए. Bazel में, मॉड्यूल को एक टारगेट से दिखाया जाता है. यह टारगेट, java_library या go_binary जैसी बिल्ड की जा सकने वाली यूनिट तय करता है. एक स्थिति में, पूरे प्रोजेक्ट को एक ही मॉड्यूल में शामिल किया जा सकता है. इसके लिए, रूट में एक BUILD फ़ाइल डालकर, उस प्रोजेक्ट की सभी सोर्स फ़ाइलों को एक साथ ग्लोब किया जा सकता है. दूसरी स्थिति में, लगभग हर सोर्स फ़ाइल को अपना मॉड्यूल बनाया जा सकता है. इसके लिए, हर फ़ाइल को BUILD फ़ाइल में, उन सभी फ़ाइलों की सूची शामिल करनी होगी जिन पर वह डिपेंड करती है.

ज़्यादातर प्रोजेक्ट, इन दोनों स्थितियों के बीच में कहीं होते हैं. साथ ही, इस विकल्प में परफ़ॉर्मेंस और रखरखाव के बीच समझौता करना पड़ता है. पूरे प्रोजेक्ट के लिए एक मॉड्यूल का इस्तेमाल करने का मतलब है कि आपको एक्सटर्नल डिपेंडेंसी जोड़ने के अलावा, BUILD फ़ाइल में कभी भी बदलाव करने की ज़रूरत नहीं पड़ेगी. हालांकि, इसका मतलब यह है कि बिल्ड सिस्टम को हमेशा पूरे प्रोजेक्ट को एक साथ बनाना होगा. इसका मतलब है कि यह बिल्ड के हिस्सों को पैरललाइज़ या डिस्ट्रिब्यूट नहीं कर पाएगा. साथ ही, यह उन हिस्सों को भी कैश नहीं कर पाएगा जिन्हें यह पहले ही बना चुका है. हर फ़ाइल के लिए एक मॉड्यूल, इसके उलट है. इसमें बिल्ड सिस्टम को कैश करने और बिल्ड के चरणों को शेड्यूल करने में ज़्यादा फ़्लेक्सिबिलिटी मिलती है. हालांकि, इंजीनियरों को डिपेंडेंसी की सूचियां बनाए रखने में ज़्यादा मेहनत करनी पड़ती है. ऐसा तब करना पड़ता है, जब वे यह बदलते हैं कि कौनसी फ़ाइलें, किन फ़ाइलों का रेफ़रंस देती हैं.

हालांकि, सटीक ग्रैन्युलैरिटी, भाषा के हिसाब से अलग-अलग होती है. साथ ही, अक्सर एक ही भाषा में भी अलग-अलग होती है. Google, टास्क पर आधारित बिल्ड सिस्टम में आम तौर पर लिखे जाने वाले मॉड्यूल की तुलना में, काफ़ी छोटे मॉड्यूल इस्तेमाल करता है. Google में, आम तौर पर प्रोडक्शन बाइनरी, अक्सर दसियों हज़ार टारगेट पर डिपेंड करती है. वहीं, सामान्य साइज़ वाली टीम भी अपने कोडबेस में कई सौ टारगेट की मालिक हो सकती है. Java जैसी भाषाओं के लिए, जिनमें पैकेजिंग की सुविधा पहले से मौजूद होती है, हर डायरेक्ट्री में आम तौर पर एक पैकेज, टारगेट, और BUILD फ़ाइल होती है. Bazel पर आधारित एक और बिल्ड सिस्टम, Pants इसे 1:1:1 नियम कहता है. जिन भाषाओं में पैकेजिंग के नियम कमज़ोर होते हैं उनमें अक्सर हर BUILD फ़ाइल के लिए कई टारगेट तय किए जाते हैं.

बिल्ड के छोटे टारगेट के फ़ायदे, बड़े पैमाने पर काम करने पर ही दिखते हैं. ऐसा इसलिए, क्योंकि इनसे डिस्ट्रिब्यूटेड बिल्ड तेज़ी से होते हैं और टारगेट को फिर से बनाने की ज़रूरत कम पड़ती है. टेस्टिंग शुरू होने के बाद, ये फ़ायदे और भी अहम हो जाते हैं. ऐसा इसलिए, क्योंकि फ़ाइन-ग्रेन्ड टारगेट का मतलब है कि बिल्ड सिस्टम, सिर्फ़ टेस्ट के सीमित सबसेट को चलाने के बारे में ज़्यादा स्मार्ट हो सकता है. इन सबसेट पर, किसी भी बदलाव का असर पड़ सकता है. Google का मानना है कि छोटे टारगेट का इस्तेमाल करने से, सिस्टम को फ़ायदे मिलते हैं. इसलिए, हमने डेवलपर पर बोझ न पड़े, इसके लिए BUILD फ़ाइलों को अपने-आप मैनेज करने वाले टूल में निवेश करके, इसके नुकसान को कम करने की कोशिश की है.

buildifier और buildozer जैसे इनमें से कुछ टूल, Bazel के साथ buildtoolsडायरेक्ट्री में उपलब्ध हैं.

मॉड्यूल की विज़िबिलिटी कम करना

Bazel और अन्य बिल्ड सिस्टम, हर टारगेट के लिए विज़िबिलिटी तय करने की अनुमति देते हैं. यह एक ऐसी प्रॉपर्टी है जिससे यह तय होता है कि अन्य कौनसे टारगेट इस पर डिपेंड कर सकते हैं. प्राइवेट टारगेट का रेफ़रंस, सिर्फ़ उसकी BUILD फ़ाइल में लिया जा सकता है. कोई टारगेट, BUILD फ़ाइलों की साफ़ तौर पर तय की गई सूची के टारगेट को ज़्यादा विज़िबिलिटी दे सकता है. वहीं, सार्वजनिक विज़िबिलिटी के मामले में, यह वर्कस्पेस में मौजूद हर टारगेट को विज़िबिलिटी दे सकता है.

ज़्यादातर प्रोग्रामिंग भाषाओं की तरह, आम तौर पर विज़िबिलिटी को जितना हो सके उतना कम रखना बेहतर होता है. आम तौर पर, Google में टीमें, टारगेट को सार्वजनिक तब ही बनाती हैं, जब वे टारगेट, Google में किसी भी टीम के लिए उपलब्ध, बड़े पैमाने पर इस्तेमाल की जाने वाली लाइब्रेरी को दिखाते हैं. जिन टीमों को अपने कोड का इस्तेमाल करने से पहले, दूसरों के साथ कोऑर्डिनेट करने की ज़रूरत होती है वे ग्राहक टारगेट की अनुमति वाली सूची को, अपने टारगेट की विज़िबिलिटी के तौर पर बनाए रखेंगी. हर टीम के इंटरनल इंप्लीमेंटेशन टारगेट, सिर्फ़ उन डायरेक्ट्री तक सीमित रहेंगे जिनका मालिकाना हक टीम के पास है. साथ ही, ज़्यादातर BUILD फ़ाइलों में सिर्फ़ एक ऐसा टारगेट होगा जो प्राइवेट नहीं है.

डिपेंडेंसी मैनेज करना

मॉड्यूल को एक-दूसरे का रेफ़रंस लेना चाहिए. कोडबेस को फ़ाइन-ग्रेन्ड मॉड्यूल में बांटने का नुकसान यह है कि आपको उन मॉड्यूल के बीच डिपेंडेंसी मैनेज करनी पड़ती है. हालांकि, टूल की मदद से इसे ऑटोमेट किया जा सकता है. इन डिपेंडेंसी को दिखाने के लिए, आम तौर पर BUILD फ़ाइल में ज़्यादातर कॉन्टेंट शामिल होता है.

इंटरनल डिपेंडेंसी

फ़ाइन-ग्रेन्ड मॉड्यूल में बांटे गए बड़े प्रोजेक्ट में, ज़्यादातर डिपेंडेंसी इंटरनल होती हैं. इसका मतलब है कि वे उसी सोर्स रिपॉज़िटरी में तय और बिल्ड किए गए किसी दूसरे टारगेट पर होती हैं. इंटरनल डिपेंडेंसी, एक्सटर्नल डिपेंडेंसी से अलग होती हैं. ऐसा इसलिए, क्योंकि इन्हें सोर्स से बनाया जाता है. वहीं, एक्सटर्नल डिपेंडेंसी को बिल्ड चलाते समय, पहले से बने आर्टफ़ैक्ट के तौर पर डाउनलोड किया जाता है. इसका मतलब यह भी है कि इंटरनल डिपेंडेंसी के लिए “वर्शन” का कोई कॉन्सेप्ट नहीं है. किसी टारगेट और उसकी सभी इंटरनल डिपेंडेंसी को रिपॉज़िटरी में हमेशा एक ही कमिट/रिविज़न पर बिल्ड किया जाता है. इंटरनल डिपेंडेंसी के मामले में, एक समस्या का ध्यान रखना चाहिए. यह समस्या, ट्रांज़िटिव डिपेंडेंसी (पहली इमेज) को मैनेज करने से जुड़ी है. मान लें कि टारगेट A, टारगेट B पर डिपेंड करता है. वहीं, टारगेट B, एक सामान्य लाइब्रेरी टारगेट C पर डिपेंड करता है. क्या टारगेट A, टारगेट C में तय की गई क्लास का इस्तेमाल कर पाएगा?

ट्रांज़िटिव डिपेंडेंसी

पहली इमेज. ट्रांज़िटिव डिपेंडेंसी

इससे जुड़े टूल के हिसाब से, इसमें कोई समस्या नहीं है. टारगेट A को बिल्ड करने पर, B और C, दोनों को टारगेट A से लिंक किया जाएगा. इसलिए, C में तय किए गए सभी सिंबल, A को पता होंगे. Bazel ने कई सालों तक इसकी अनुमति दी. हालांकि, Google के बढ़ने के साथ-साथ, हमें समस्याएं दिखने लगीं. मान लें कि B को इस तरह से रीफ़ैक्टर किया गया कि अब उसे C पर डिपेंड करने की ज़रूरत नहीं है. अगर B की C पर डिपेंडेंसी हटा दी गई, तो A और कोई भी दूसरा टारगेट जो B पर डिपेंडेंसी के ज़रिए C का इस्तेमाल करता है वह काम करना बंद कर देगा. असल में, किसी टारगेट की डिपेंडेंसी, उसके सार्वजनिक कॉन्ट्रैक्ट का हिस्सा बन गईं और उन्हें कभी भी सुरक्षित तरीके से बदला नहीं जा सका. इसका मतलब है कि समय के साथ-साथ डिपेंडेंसी बढ़ती गईं और Google में बिल्ड की प्रोसेस धीमी होने लगी.

Google ने Bazel में “स्ट्रिक्ट ट्रांज़िटिव डिपेंडेंसी मोड” शुरू करके, इस समस्या को हल किया. इस मोड में, Bazel यह पता लगाता है कि कोई टारगेट, सीधे तौर पर उस पर डिपेंड किए बिना किसी सिंबल का रेफ़रंस लेने की कोशिश करता है या नहीं. अगर ऐसा होता है, तो यह गड़बड़ी और शेल कमांड के साथ फ़ेल हो जाता है. इस कमांड का इस्तेमाल करके, डिपेंडेंसी को अपने-आप जोड़ा जा सकता है. Google के पूरे कोडबेस में इस बदलाव को लागू करना और बिल्ड के लाखों टारगेट को रीफ़ैक्टर करके, उनकी डिपेंडेंसी को साफ़ तौर पर दिखाना, कई सालों की कोशिश थी. हालांकि, यह कोशिश कामयाब रही. अब हमारे बिल्ड ज़्यादा तेज़ी से होते हैं, क्योंकि टारगेट में गैर-ज़रूरी डिपेंडेंसी कम होती हैं. साथ ही, इंजीनियर उन डिपेंडेंसी को हटा सकते हैं जिनकी उन्हें ज़रूरत नहीं है. इसके लिए, उन्हें उन टारगेट के काम करना बंद करने की चिंता करने की ज़रूरत नहीं है जो उन पर डिपेंड करते हैं.

हमेशा की तरह, स्ट्रिक्ट ट्रांज़िटिव डिपेंडेंसी लागू करने में समझौता करना पड़ा. इससे बिल्ड फ़ाइलें ज़्यादा वर्बोस हो गईं, क्योंकि अक्सर इस्तेमाल की जाने वाली लाइब्रेरी को अब कई जगहों पर साफ़ तौर पर दिखाना पड़ता है. वहीं, इंजीनियरों को BUILD फ़ाइलों में डिपेंडेंसी जोड़ने में ज़्यादा मेहनत करनी पड़ी. इसके बाद से, हमने ऐसे टूल बनाए हैं जिनसे इस मेहनत को कम किया जा सकता है. ये टूल, कई डिपेंडेंसी को अपने-आप ढूंढकर, उन्हें डेवलपर की मदद के बिना BUILD फ़ाइलों में जोड़ देते हैं. हालांकि, ऐसे टूल के बिना भी, हमने पाया है कि कोडबेस के बढ़ने पर, यह समझौता फ़ायदेमंद है. BUILD फ़ाइल में साफ़ तौर पर डिपेंडेंसी जोड़ना, एक बार की लागत है. वहीं, इंप्लिसिट ट्रांज़िटिव डिपेंडेंसी से, बिल्ड टारगेट के मौजूद रहने तक समस्याएं हो सकती हैं. Bazel डिफ़ॉल्ट रूप से Java कोड पर स्ट्रिक्ट ट्रांज़िटिव डिपेंडेंसी लागू करता है.

एक्सटर्नल डिपेंडेंसी

अगर कोई डिपेंडेंसी इंटरनल नहीं है, तो वह एक्सटर्नल होनी चाहिए. एक्सटर्नल डिपेंडेंसी, उन आर्टफ़ैक्ट पर होती हैं जिन्हें बिल्ड सिस्टम के बाहर बनाया और सेव किया जाता है. डिपेंडेंसी को सीधे तौर पर आर्टफ़ैक्ट रिपॉज़िटरी से इंपोर्ट किया जाता है. आम तौर पर, इसे इंटरनेट से ऐक्सेस किया जाता है. साथ ही, इसे सोर्स से बनाने के बजाय, पहले से बने आर्टफ़ैक्ट के तौर पर इस्तेमाल किया जाता है. एक्सटर्नल और इंटरनल डिपेंडेंसी के बीच सबसे बड़े अंतरों में से एक यह है कि एक्सटर्नल डिपेंडेंसी के वर्शन होते हैं. साथ ही, ये वर्शन, प्रोजेक्ट के सोर्स कोड से अलग होते हैं.

डिपेंडेंसी मैनेजमेंट के लिए, ऑटोमेटेड बनाम मैन्युअल तरीका

बिल्ड सिस्टम, एक्सटर्नल डिपेंडेंसी के वर्शन को मैन्युअल या ऑटोमेटेड तरीके से मैनेज करने की अनुमति दे सकते हैं. मैन्युअल तरीके से मैनेज करने पर, बिल्डफ़ाइल में उस वर्शन को साफ़ तौर पर दिखाया जाता है जिसे आर्टफ़ैक्ट रिपॉज़िटरी से डाउनलोड करना है, इसके लिए, अक्सर सिमेंटिक वर्शन स्ट्रिंग का इस्तेमाल किया जाता है. जैसे 1.1.4. ऑटोमेटेड तरीके से मैनेज करने पर, सोर्स फ़ाइल में स्वीकार किए जा सकने वाले वर्शन की रेंज तय की जाती है. साथ ही, बिल्ड सिस्टम हमेशा सबसे नया वर्शन डाउनलोड करता है. उदाहरण के लिए, Gradle में डिपेंडेंसी वर्शन को “1.+” के तौर पर तय किया जा सकता है. इससे यह तय होता है कि डिपेंडेंसी का कोई भी माइनर या पैच वर्शन स्वीकार किया जा सकता है, बशर्ते कि मेजर वर्शन 1 हो.

ऑटोमेटेड तरीके से मैनेज की गई डिपेंडेंसी, छोटे प्रोजेक्ट के लिए काम की हो सकती हैं. हालांकि, ये आम तौर पर बड़े प्रोजेक्ट या एक से ज़्यादा इंजीनियरों के साथ काम करने वाले प्रोजेक्ट के लिए समस्याएं पैदा करती हैं. ऑटोमेटेड तरीके से मैनेज की गई डिपेंडेंसी की समस्या यह है कि वर्शन कब अपडेट होगा, इस पर आपका कोई कंट्रोल नहीं होता. इस बात की कोई गारंटी नहीं है कि बाहरी पक्ष, बड़े अपडेट नहीं करेंगे. भले ही, वे सिमेंटिक वर्शनिंग का इस्तेमाल करने का दावा करें. इसलिए, एक दिन काम करने वाला बिल्ड, अगले दिन काम करना बंद कर सकता है. साथ ही, यह पता लगाना मुश्किल हो सकता है कि क्या बदला है या इसे वापस काम करने की स्थिति में कैसे लाया जाए. भले ही, बिल्ड काम करना बंद न करे, लेकिन इसमें मामूली बदलाव या परफ़ॉर्मेंस में बदलाव हो सकते हैं. इन्हें ट्रैक करना मुश्किल होता है.

इसके उलट, मैन्युअल तरीके से मैनेज की गई डिपेंडेंसी के लिए, सोर्स कंट्रोल में बदलाव करना ज़रूरी होता है. इसलिए, इन्हें आसानी से खोजा और वापस लाया जा सकता है. साथ ही, पुरानी डिपेंडेंसी के साथ बिल्ड करने के लिए, रिपॉज़िटरी का पुराना वर्शन चेक आउट किया जा सकता है. Bazel के लिए ज़रूरी है कि सभी डिपेंडेंसी के वर्शन को मैन्युअल तरीके से तय किया जाए. सामान्य पैमाने पर भी, मैन्युअल तरीके से वर्शन मैनेज करने की लागत, इससे मिलने वाली स्थिरता के लिए फ़ायदेमंद है.

एक वर्शन का नियम

किसी लाइब्रेरी के अलग-अलग वर्शन को आम तौर पर अलग-अलग आर्टफ़ैक्ट से दिखाया जाता है. इसलिए, सिद्धांत के तौर पर, एक ही एक्सटर्नल डिपेंडेंसी के अलग-अलग वर्शन को बिल्ड सिस्टम में अलग-अलग नामों से तय करने में कोई समस्या नहीं है. इस तरह, हर टारगेट यह चुन सकता है कि उसे डिपेंडेंसी का कौनसा वर्शन इस्तेमाल करना है. असल में, इससे कई समस्याएं होती हैं. इसलिए, Google अपने कोडबेस में तीसरे पक्ष की सभी डिपेंडेंसी के लिए, एक वर्शन कानियम लागू करता है.

कई वर्शन की अनुमति देने की सबसे बड़ी समस्या, डायमंड डिपेंडेंसी की समस्या है. मान लें कि टारगेट A, टारगेट B और किसी एक्सटर्नल लाइब्रेरी के v1 पर डिपेंड करता है. अगर बाद में टारगेट B को रीफ़ैक्टर करके, उसी एक्सटर्नल लाइब्रेरी के v2 पर डिपेंडेंसी जोड़ी जाती है, तो टारगेट A काम करना बंद कर देगा. ऐसा इसलिए, क्योंकि अब यह उसी लाइब्रेरी के दो अलग-अलग वर्शन पर इंप्लिसिट तरीके से डिपेंड करता है. असल में, किसी टारगेट से तीसरे पक्ष की ऐसी लाइब्रेरी में नई डिपेंडेंसी जोड़ना कभी भी सुरक्षित नहीं होता जिसके कई वर्शन हों. ऐसा इसलिए, क्योंकि उस टारगेट के उपयोगकर्ता पहले से ही किसी दूसरे वर्शन पर डिपेंड कर सकते हैं. एक वर्शन के नियम का पालन करने से, यह टकराव नहीं हो सकता. अगर कोई टारगेट, तीसरे पक्ष की लाइब्रेरी पर डिपेंडेंसी जोड़ता है, तो मौजूदा सभी डिपेंडेंसी पहले से ही उसी वर्शन पर होंगी. इसलिए, वे एक साथ काम कर सकती हैं.

ट्रांज़िटिव एक्सटर्नल डिपेंडेंसी

एक्सटर्नल डिपेंडेंसी की ट्रांज़िटिव डिपेंडेंसी को मैनेज करना मुश्किल हो सकता है. Maven Central जैसी कई आर्टफ़ैक्ट रिपॉज़िटरी, आर्टफ़ैक्ट को रिपॉज़िटरी में मौजूद अन्य आर्टफ़ैक्ट के खास वर्शन पर डिपेंडेंसी तय करने की अनुमति देती हैं. Maven या Gradle जैसे बिल्ड टूल, डिफ़ॉल्ट रूप से हर ट्रांज़िटिव डिपेंडेंसी को बार-बार डाउनलोड करते हैं. इसका मतलब है कि आपके प्रोजेक्ट में एक डिपेंडेंसी जोड़ने से, कुल मिलाकर दर्जनों आर्टफ़ैक्ट डाउनलोड हो सकते हैं.

यह बहुत काम का है: नई लाइब्रेरी पर डिपेंडेंसी जोड़ते समय, उस लाइब्रेरी की हर ट्रांज़िटिव डिपेंडेंसी को ट्रैक करना और उन्हें मैन्युअल तरीके से जोड़ना मुश्किल होगा. हालांकि, इसका एक बड़ा नुकसान भी है: अलग-अलग लाइब्रेरी, तीसरे पक्ष की एक ही लाइब्रेरी के अलग-अलग वर्शन पर डिपेंड कर सकती हैं. इसलिए, यह रणनीति, एक वर्शन के नियम का उल्लंघन करती है और इससे डायमंड डिपेंडेंसी की समस्या होती है. अगर आपका टारगेट, दो एक्सटर्नल लाइब्रेरी पर डिपेंड करता है जो एक ही डिपेंडेंसी के अलग-अलग वर्शन का इस्तेमाल करती हैं, तो यह नहीं कहा जा सकता कि आपको कौनसा वर्शन मिलेगा. इसका मतलब यह भी है कि एक्सटर्नल डिपेंडेंसी को अपडेट करने से, कोडबेस में ऐसी समस्याएं हो सकती हैं जो देखने में एक-दूसरे से जुड़ी नहीं हैं. ऐसा तब हो सकता है, जब नया वर्शन, अपनी कुछ डिपेंडेंसी के ऐसे वर्शन को पुल करना शुरू कर दे जो एक-दूसरे से टकराते हैं.

Bazel, ट्रांज़िटिव डिपेंडेंसी को अपने-आप डाउनलोड नहीं करता था. यह WORKSPACE फ़ाइल का इस्तेमाल करता था. इसके लिए, सभी ट्रांज़िटिव डिपेंडेंसी को सूची में शामिल करना ज़रूरी था. इससे एक्सटर्नल डिपेंडेंसी मैनेज करने में काफ़ी परेशानी होती थी. Bazel ने MODULE.bazel फ़ाइल के तौर पर, ट्रांज़िटिव एक्सटर्नल डिपेंडेंसी को अपने-आप मैनेज करने की सुविधा जोड़ी है. ज़्यादा जानकारी के लिए, एक्सटर्नल डिपेंडेंसी की खास जानकारी देखें.

यहां भी, विकल्प सुविधा और स्केलेबिलिटी के बीच है. छोटे प्रोजेक्ट, ट्रांज़िटिव डिपेंडेंसी को खुद मैनेज करने की चिंता नहीं करना चाहेंगे. साथ ही, वे ट्रांज़िटिव डिपेंडेंसी को अपने-आप मैनेज करने की सुविधा का इस्तेमाल कर सकते हैं. संगठन और कोडबेस के बढ़ने के साथ-साथ, यह रणनीति कम और कम काम की होती जाती है. साथ ही, टकराव और अनचाहे नतीजे ज़्यादा और ज़्यादा बार होने लगते हैं. बड़े पैमाने पर, डिपेंडेंसी को मैन्युअल तरीके से मैनेज करने की लागत, डिपेंडेंसी को अपने-आप मैनेज करने की सुविधा की वजह से होने वाली समस्याओं से निपटने की लागत से काफ़ी कम होती है.

एक्सटर्नल डिपेंडेंसी का इस्तेमाल करके, बिल्ड के नतीजों को कैश करना

एक्सटर्नल डिपेंडेंसी, अक्सर तीसरे पक्ष से मिलती हैं. ये तीसरे पक्ष, लाइब्रेरी के स्टेबल वर्शन रिलीज़ करते हैं. हो सकता है कि ये सोर्स कोड उपलब्ध न कराएं. कुछ संगठन, अपने कुछ कोड को आर्टफ़ैक्ट के तौर पर भी उपलब्ध करा सकते हैं. इससे कोड के अन्य हिस्सों को, इंटरनल डिपेंडेंसी के बजाय तीसरे पक्ष की डिपेंडेंसी के तौर पर उन पर डिपेंड करने की अनुमति मिलती है. अगर आर्टफ़ैक्ट को बिल्ड करने में ज़्यादा समय लगता है, लेकिन उन्हें डाउनलोड करने में कम समय लगता है, तो इससे बिल्ड की प्रोसेस तेज़ हो सकती है.

हालांकि, इससे काफ़ी लागत और जटिलता भी बढ़ती है: किसी को उन सभी आर्टफ़ैक्ट को बिल्ड करने और उन्हें आर्टफ़ैक्ट रिपॉज़िटरी में अपलोड करने की ज़िम्मेदारी लेनी होगी. साथ ही, क्लाइंट को यह पक्का करना होगा कि वे सबसे नए वर्शन के साथ अप-टू-डेट रहें. डीबग करना भी ज़्यादा मुश्किल हो जाता है, क्योंकि सिस्टम के अलग-अलग हिस्सों को रिपॉज़िटरी में अलग-अलग पॉइंट से बिल्ड किया गया होगा. साथ ही, सोर्स ट्री का कोई एक जैसा व्यू नहीं होगा.

आर्टफ़ैक्ट को बिल्ड करने में ज़्यादा समय लगने की समस्या को हल करने का एक बेहतर तरीका है कि रिमोट कैशिंग की सुविधा देने वाले बिल्ड सिस्टम का इस्तेमाल किया जाए. इसके बारे में पहले बताया जा चुका है. ऐसा बिल्ड सिस्टम, हर बिल्ड से मिलने वाले आर्टफ़ैक्ट को ऐसी जगह पर सेव करता है जिसे इंजीनियरों के साथ शेयर किया जाता है. इसलिए, अगर कोई डेवलपर किसी ऐसे आर्टफ़ैक्ट पर डिपेंड करता है जिसे हाल ही में किसी और ने बिल्ड किया है, तो बिल्ड सिस्टम उसे बिल्ड करने के बजाय अपने-आप डाउनलोड कर लेता है. इससे आर्टफ़ैक्ट पर सीधे तौर पर डिपेंड करने के सभी परफ़ॉर्मेंस फ़ायदे मिलते हैं. साथ ही, यह पक्का होता है कि बिल्ड, उसी सोर्स से हमेशा बिल्ड किए जाने की तरह ही एक जैसे हों. Google, इंटरनल तौर पर इसी रणनीति का इस्तेमाल करता है. साथ ही, Bazel को रिमोट कैश का इस्तेमाल करने के लिए कॉन्फ़िगर किया जा सकता है.

एक्सटर्नल डिपेंडेंसी की सुरक्षा और भरोसेमंद होना

तीसरे पक्ष के सोर्स से आर्टफ़ैक्ट पर डिपेंड करना, स्वाभाविक रूप से जोखिम भरा होता है. अगर तीसरे पक्ष का सोर्स (जैसे, आर्टफ़ैक्ट रिपॉज़िटरी) काम करना बंद कर देता है, तो उपलब्धता का जोखिम होता है. ऐसा इसलिए, क्योंकि अगर आपका बिल्ड, एक्सटर्नल डिपेंडेंसी डाउनलोड नहीं कर पाता है, तो वह पूरी तरह से रुक सकता है. सुरक्षा का जोखिम भी होता है: अगर तीसरे पक्ष के सिस्टम से कोई हमलावर समझौता कर लेता है, तो वह रेफ़रंस किए गए आर्टफ़ैक्ट को अपने डिज़ाइन के आर्टफ़ैक्ट से बदल सकता है. इससे वह आपके बिल्ड में कोई भी कोड इंजेक्ट कर सकता है. इन दोनों समस्याओं को कम किया जा सकता है. इसके लिए, जिन आर्टफ़ैक्ट पर आप डिपेंड करते हैं उन्हें अपने कंट्रोल वाले सर्वर पर मिरर करें. साथ ही, अपने बिल्ड सिस्टम को Maven Central जैसी तीसरे पक्ष की आर्टफ़ैक्ट रिपॉज़िटरी को ऐक्सेस करने से रोकें. हालांकि, इन मिरर को बनाए रखने के लिए मेहनत और संसाधनों की ज़रूरत होती है. इसलिए, इनका इस्तेमाल करना है या नहीं, यह अक्सर प्रोजेक्ट के स्केल पर निर्भर करता है. सुरक्षा की समस्या को कम लागत में पूरी तरह से रोका जा सकता है. इसके लिए, सोर्स रिपॉज़िटरी में तीसरे पक्ष के हर आर्टफ़ैक्ट का हैश तय करना ज़रूरी है. इससे, आर्टफ़ैक्ट में छेड़छाड़ होने पर, बिल्ड फ़ेल हो जाएगा. एक और विकल्प है. इससे इस समस्या को पूरी तरह से टाला जा सकता है. इसके लिए, अपने प्रोजेक्ट की डिपेंडेंसी को वेंडर करें. जब कोई प्रोजेक्ट अपनी डिपेंडेंसी को वेंडर करता है, तो वह उन्हें प्रोजेक्ट के सोर्स कोड के साथ सोर्स कंट्रोल में चेक इन करता है. इसके लिए, सोर्स या बाइनरी के तौर पर चेक इन किया जाता है. इसका मतलब है कि प्रोजेक्ट की सभी एक्सटर्नल डिपेंडेंसी, इंटरनल डिपेंडेंसी में बदल जाती हैं. Google, इंटरनल तौर पर इस तरीके का इस्तेमाल करता है. इसके लिए, Google के सोर्स ट्री के रूट में मौजूद third_party डायरेक्ट्री में, Google में रेफ़रंस की गई तीसरे पक्ष की हर लाइब्रेरी को चेक इन किया जाता है. हालांकि, Google में यह तरीका सिर्फ़ इसलिए काम करता है, क्योंकि Google का सोर्स कंट्रोल सिस्टम, बहुत बड़े मोनोरिपो को मैनेज करने के लिए कस्टम तौर पर बनाया गया है. इसलिए, हो सकता है कि वेंडरिंग, सभी संगठनों के लिए विकल्प न हो.