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