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

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

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

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 फ़ाइलों की सूची में शामिल किया जा सकता है. इससे, टारगेट की गई फ़ाइल ज़्यादा लोगों को दिखती है. इसके अलावा, अगर फ़ाइल को सार्वजनिक तौर पर दिखने के लिए सेट किया जाता है, तो वह Workspace में मौजूद सभी लोगों को दिखती है.

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

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

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

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

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

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

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

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

Google ने इस समस्या को हल करने के लिए, Bazel में "strict transitive dependency mode" लॉन्च किया. इस मोड में, 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 का सोर्स कंट्रोल सिस्टम, बहुत बड़े मोनोरिपो को मैनेज करने के लिए कस्टम तौर पर बनाया गया है. इसलिए, वेंडरिंग सभी संगठनों के लिए एक विकल्प नहीं हो सकता.