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

किसी समस्या की शिकायत करें सोर्स देखें Nightly · 7.4 .

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

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

बेज़ल जैसे आर्टफ़ैक्ट-आधारित बिल्ड सिस्टम का इस्तेमाल करने वाले प्रोजेक्ट, मॉड्यूल के एक सेट में बंटे होते हैं. इनमें ऐसे मॉड्यूल होते हैं जो 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, buildtools डायरेक्ट्री में Bazel के साथ उपलब्ध हैं.

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

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

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

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

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

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

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

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

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

जहां तक टूल की बात है, इसमें कोई समस्या नहीं है. 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 हो.

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

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

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

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

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

ट्रांसिटिव बाहरी डिपेंडेंसी

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

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

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

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

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

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

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

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

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

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