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

किसी समस्या की शिकायत करें सोर्स देखें Nightly · 8.0 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

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

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, 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 हो.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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