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

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

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

मॉड्यूल और डिपेंडेंसी चुनना

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

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

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

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

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

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

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

मॉड्यूल दिखने की संभावना कम करना

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

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

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

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

अपने-आप बनाम मैन्युअल डिपेंडेंसी मैनेजमेंट

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

बाहरी डिपेंडेंसी की सुरक्षा और विश्वसनीयता

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