पिछले पेजों को देखने पर, एक थीम बार-बार दोहराई जाती है: अपने कोड को मैनेज करना काफ़ी आसान है, लेकिन इसकी डिपेंडेंसी को मैनेज करना बहुत मुश्किल है. ये कई तरह की डिपेंडेंसी होती हैं: कभी-कभी किसी टास्क पर डिपेंडेंसी होती है. जैसे, “रिलीज़ को पूरा होने के तौर पर मार्क करने से पहले, दस्तावेज़ को पुश करें”. कभी-कभी किसी आर्टफ़ैक्ट पर डिपेंडेंसी होती है. जैसे, “मुझे अपना कोड बनाने के लिए, कंप्यूटर विज़न लाइब्रेरी के नए वर्शन की ज़रूरत है”. कभी-कभी, आपके कोडबेस के किसी दूसरे हिस्से पर इंटरनल डिपेंडेंसी होती है. कभी-कभी, आपके पास किसी दूसरी टीम (आपके संगठन या तीसरे पक्ष की) के मालिकाना हक वाले कोड या डेटा पर एक्सटर्नल डिपेंडेंसी होती है. हालांकि, “मुझे यह चाहिए, ताकि मैं इसे पा सकूं” का सिद्धांत, बिल्ड सिस्टम के डिज़ाइन में बार-बार दोहराया जाता है. साथ ही, डिपेंडेंसी मैनेज करना, शायद बिल्ड सिस्टम का सबसे बुनियादी काम है.
मॉड्यूल और डिपेंडेंसी से जुड़ी समस्याएं हल करना
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 पर निर्भर करता है. साथ ही, टारगेट B, कॉमन लाइब्रेरी टारगेट C पर निर्भर करता है. क्या टारगेट A, टारगेट C में तय की गई क्लास का इस्तेमाल कर सकता है?
पहली इमेज. ट्रांज़िटिव डिपेंडेंसी
बुनियादी टूल के हिसाब से, इसमें कोई समस्या नहीं है. जब टारगेट A बनाया जाएगा, तब B और C, दोनों को टारगेट A में लिंक किया जाएगा. इसलिए, C में तय किए गए सभी सिंबल, A को पता होंगे. Bazel ने कई सालों तक इसकी अनुमति दी थी. हालांकि, Google के बढ़ने के साथ-साथ हमें समस्याएं दिखने लगीं. मान लें कि B को इस तरह से रीफ़ैक्टर किया गया है कि अब उसे C पर निर्भर रहने की ज़रूरत नहीं है. अगर इसके बाद, B की C पर निर्भरता हटा दी जाती है, तो A और ऐसे सभी टारगेट काम नहीं करेंगे जो B पर निर्भरता के ज़रिए 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, ट्रांज़िटिव डिपेंडेंसी को अपने-आप डाउनलोड नहीं करता था. इसमें WORKSPACE
फ़ाइल का इस्तेमाल किया जाता था. इसमें सभी ट्रांज़िटिव डिपेंडेंसी को लिस्ट करना ज़रूरी होता था. इस वजह से, बाहरी डिपेंडेंसी को मैनेज करने में काफ़ी परेशानी होती थी. Bazel ने तब से, MODULE.bazel
फ़ाइल के तौर पर, बाहरी डिपेंडेंसी को अपने-आप मैनेज करने की सुविधा जोड़ी है. ज़्यादा जानकारी के लिए, बाहरी डिपेंडेंसी की खास जानकारी देखें.
हालांकि, यहां भी सुविधा और स्केलेबिलिटी के बीच किसी एक को चुनना होता है. छोटे प्रोजेक्ट में, ट्रांज़िटिव डिपेंडेंसी को मैनेज करने की ज़रूरत नहीं होती. इसलिए, वे अपने-आप मैनेज होने वाली ट्रांज़िटिव डिपेंडेंसी का इस्तेमाल कर सकते हैं. संगठन और कोडबेस के बढ़ने के साथ-साथ, यह रणनीति कम से कम आकर्षक होती जाती है. साथ ही, टकराव और अनचाहे नतीजे बार-बार मिलते हैं. बड़े पैमाने पर, डिपेंडेंसी को मैन्युअल तरीके से मैनेज करने की लागत, डिपेंडेंसी को अपने-आप मैनेज करने की सुविधा से जुड़ी समस्याओं को ठीक करने की लागत से काफ़ी कम होती है.
बाहरी डिपेंडेंसी का इस्तेमाल करके, बिल्ड के नतीजों को कैश मेमोरी में सेव करना
बाहरी डिपेंडेंसी, अक्सर तीसरे पक्ष के ऐसे ऐप्लिकेशन उपलब्ध कराते हैं जो लाइब्रेरी के स्टेबल वर्शन रिलीज़ करते हैं. ऐसा हो सकता है कि वे सोर्स कोड उपलब्ध न कराएं. कुछ संगठन, अपने कुछ कोड को आर्टफ़ैक्ट के तौर पर उपलब्ध कराने का विकल्प भी चुन सकते हैं. इससे कोड के अन्य हिस्सों को इंटरनल डिपेंडेंसी के बजाय, तीसरे पक्ष के तौर पर उन पर निर्भर रहने की अनुमति मिलती है. अगर आर्टफ़ैक्ट को बनाने में समय लगता है, लेकिन डाउनलोड करने में कम समय लगता है, तो इससे बिल्ड प्रोसेस को तेज़ी से पूरा किया जा सकता है.
हालांकि, इससे कई तरह की समस्याएं भी आती हैं. जैसे, हर आर्टफ़ैक्ट को बनाने और उसे आर्टफ़ैक्ट रिपॉज़िटरी में अपलोड करने की ज़िम्मेदारी किसी व्यक्ति को लेनी होगी. साथ ही, क्लाइंट को यह पक्का करना होगा कि वे अप-टू-डेट वर्शन का इस्तेमाल कर रहे हैं. डीबग करना भी बहुत मुश्किल हो जाता है, क्योंकि सिस्टम के अलग-अलग हिस्सों को रिपॉज़िटरी के अलग-अलग पॉइंट से बनाया गया होगा. साथ ही, सोर्स ट्री का एक जैसा व्यू नहीं होगा.
आर्टफ़ैक्ट बनाने में ज़्यादा समय लगने की समस्या को हल करने का बेहतर तरीका यह है कि ऐसे बिल्ड सिस्टम का इस्तेमाल किया जाए जो रिमोट कैश मेमोरी के साथ काम करता हो. इसके बारे में पहले बताया जा चुका है. इस तरह का बिल्ड सिस्टम, हर बिल्ड से मिले आर्टफ़ैक्ट को ऐसी जगह पर सेव करता है जिसे सभी इंजीनियर ऐक्सेस कर सकते हैं. इसलिए, अगर कोई डेवलपर किसी ऐसे आर्टफ़ैक्ट पर निर्भर करता है जिसे हाल ही में किसी और ने बनाया है, तो बिल्ड सिस्टम उसे बनाने के बजाय अपने-आप डाउनलोड कर लेता है. इससे, सीधे तौर पर आर्टफ़ैक्ट पर निर्भर रहने के सभी परफ़ॉर्मेंस फ़ायदे मिलते हैं. साथ ही, यह भी पक्का किया जाता है कि बिल्ड उतने ही एक जैसे हों जितने कि वे हमेशा एक ही सोर्स से बनाए जाते हैं. Google इस रणनीति का इस्तेमाल अंदरूनी तौर पर करता है. साथ ही, Bazel को रिमोट कैश का इस्तेमाल करने के लिए कॉन्फ़िगर किया जा सकता है.
बाहरी डिपेंडेंसी की सुरक्षा और भरोसेमंद होने की पुष्टि करना
तीसरे पक्ष के स्रोतों से मिले आर्टफ़ैक्ट पर भरोसा करना जोखिम भरा हो सकता है. अगर तीसरे पक्ष का सोर्स (जैसे, आर्टफ़ैक्ट रिपॉज़िटरी) काम नहीं करता है, तो उपलब्धता से जुड़ी समस्या हो सकती है. ऐसा इसलिए, क्योंकि बाहरी डिपेंडेंसी डाउनलोड न कर पाने की वजह से, आपका पूरा बिल्ड रुक सकता है. सुरक्षा से जुड़ा जोखिम भी है: अगर हमलावर तीसरे पक्ष के सिस्टम से समझौता करता है, तो वह रेफ़र किए गए आर्टफ़ैक्ट को अपने डिज़ाइन किए गए आर्टफ़ैक्ट से बदल सकता है. इससे हमलावर, आपके बिल्ड में कोई भी कोड डाल सकता है. इन दोनों समस्याओं को कम किया जा सकता है. इसके लिए, आपको जिन आर्टफ़ैक्ट की ज़रूरत है उन्हें अपने कंट्रोल वाले सर्वर पर मिरर करें. साथ ही, अपने बिल्ड सिस्टम को Maven Central जैसी तीसरे पक्ष की आर्टफ़ैक्ट रिपॉज़िटरी को ऐक्सेस करने से रोकें. हालांकि, इन मिरर को बनाए रखने में काफ़ी मेहनत और संसाधनों की ज़रूरत होती है. इसलिए, इनका इस्तेमाल करना है या नहीं, यह प्रोजेक्ट के साइज़ पर निर्भर करता है. सुरक्षा से जुड़ी समस्या को पूरी तरह से रोका जा सकता है. इसके लिए, तीसरे पक्ष के हर आर्टफ़ैक्ट के हैश को सोर्स रिपॉज़िटरी में शामिल करना ज़रूरी है. इससे, अगर आर्टफ़ैक्ट में छेड़छाड़ की जाती है, तो बिल्ड फ़ेल हो जाएगा. इस समस्या से बचने का एक और तरीका यह है कि अपने प्रोजेक्ट की डिपेंडेंसी को वेंडर करें. जब कोई प्रोजेक्ट अपनी डिपेंडेंसी को वेंडर करता है, तो वह उन्हें प्रोजेक्ट के सोर्स कोड के साथ सोर्स कंट्रोल में शामिल करता है. ऐसा सोर्स या बाइनरी के तौर पर किया जाता है. इसका मतलब है कि प्रोजेक्ट की सभी बाहरी डिपेंडेंसी को इंटरनल डिपेंडेंसी में बदल दिया जाता है. Google इस तरीके का इस्तेमाल अंदरूनी तौर पर करता है. इसमें, Google के सोर्स ट्री के रूट में मौजूद third_party
डायरेक्ट्री में, Google में रेफ़र की गई हर तीसरे पक्ष की लाइब्रेरी की जाँच की जाती है. हालांकि, यह सिर्फ़ Google में काम करता है, क्योंकि Google का सोर्स कंट्रोल सिस्टम, बहुत बड़े मोनोरिपो को मैनेज करने के लिए कस्टम तौर पर बनाया गया है. इसलिए, हो सकता है कि वेंडरिंग सभी संगठनों के लिए एक विकल्प न हो.