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

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

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

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

बारीकियां दिखाने वाले मॉड्यूल और 1:1:1 नियम का इस्तेमाल करना

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

बाहरी डिपेंडेंसी

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

बाहरी डिपेंडेंसी की सुरक्षा और भरोसा

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