การจัดการการพึ่งพา

รายงานปัญหา ดูแหล่งที่มา ตอนกลางคืน · 7.4 ที่ใช้เวลาเพียง 2 นาที 7.3 · 7.2 · 7.1 · 7.0 · 6.5

เมื่อดูหน้าก่อนหน้า คุณจะเห็นว่ามีธีมหนึ่งที่ซ้ำกันอยู่ตลอด นั่นคือ การจัดการโค้ดของคุณเองนั้นค่อนข้างตรงไปตรงมา แต่การจัดการทรัพยากรที่เกี่ยวข้องนั้นยากกว่ามาก ทรัพยากร Dependency นั้นมีมากมาย บางครั้งอาจมี การอ้างอิงในงาน (เช่น "พุชเอกสารก่อนฉันจะทำเครื่องหมายรุ่นเป็น เสร็จสมบูรณ์") และบางครั้งอาจขึ้นอยู่กับอาร์ติแฟกต์ (เช่น "ฉันต้อง มีไลบรารีคอมพิวเตอร์วิทัศน์รุ่นล่าสุดเพื่อสร้างโค้ดของฉัน") บางครั้งคุณมีทรัพยากร Dependency ภายในกับอีกส่วนของ Codebase และ บางครั้งคุณมีทรัพยากร Dependency ภายนอกในโค้ดหรือข้อมูลของทีมอื่น (ในองค์กรของคุณหรือบุคคลที่สาม) แต่ไม่ว่าในกรณีใด แนวคิดที่ว่า "ฉันต้องมีสิ่งนั้นก่อนจึงจะมีสิ่งนี้ได้" เป็นสิ่งที่เกิดขึ้นซ้ำๆ ในการออกแบบระบบบิลด์ และการจัดการทรัพยากรภายนอกอาจเป็นงานพื้นฐานที่สุดของระบบบิลด์

การจัดการกับโมดูลและการขึ้นต่อกัน

โปรเจ็กต์ที่ใช้ระบบบิลด์ที่อิงตามอาร์ติแฟกต์ เช่น Bazel จะแบ่งออกเป็นชุดของโมดูล โดยโมดูลจะแสดงความสัมพันธ์แบบพึ่งพากันผ่านBUILDไฟล์ การจัดระเบียบโมดูลและข้อกำหนดเหล่านี้อย่างเหมาะสมจะส่งผลอย่างมากต่อทั้งประสิทธิภาพของระบบบิลด์และปริมาณงานที่ต้องใช้เพื่อบำรุงรักษา

การใช้ข้อบังคับแบบละเอียดและกฎ 1:1:1

คำถามแรกที่ปรากฏขึ้นเมื่อจัดโครงสร้างบิลด์ที่อิงตามอาร์ติแฟกต์คือ ว่าแต่ละโมดูลควรมีฟังก์ชันการทำงานเท่าใด ใน Bazel โมดูลจะแสดงด้วยเป้าหมายที่ระบุหน่วยที่คอมไพล์ได้ เช่น java_library หรือ go_binary ทั้งโปรเจ็กต์อาจเป็นไปได้ ที่มีอยู่ในโมดูลเดียวโดยวางไฟล์ BUILD 1 ไฟล์ไว้ที่รูท รวบรวมข้อมูลไฟล์ต้นฉบับของโครงการนั้นทั้งหมดซ้ำๆ ที่อื่น มากเป็นพิเศษ ไฟล์ต้นฉบับเกือบทุกไฟล์สามารถสร้างเป็นโมดูลของตัวเองได้อย่างมีประสิทธิภาพ กำหนดให้แต่ละไฟล์แสดงในไฟล์ BUILD ทุกๆ ไฟล์ที่ต้องพึ่งพา

โปรเจ็กต์ส่วนใหญ่จะอยู่ตรงกลางระหว่าง 2 ลักษณะนี้ และตัวเลือกนี้เกี่ยวข้องกับการแลกเปลี่ยนระหว่างประสิทธิภาพกับความสามารถในการบำรุงรักษา การใช้โมดูลเดียวสำหรับฟังก์ชัน ทั้งโปรเจ็กต์อาจหมายความว่าคุณไม่ต้องแตะไฟล์ BUILD เลย เมื่อเพิ่มทรัพยากร Dependency ภายนอก แต่หมายความว่าระบบบิลด์ต้อง สร้างทั้งโครงการพร้อมกันเสมอ ซึ่งหมายความว่าจะไม่สามารถทำงานแบบขนานหรือกระจายบางส่วนของบิลด์ รวมถึงแคชบางส่วนที่สร้างขึ้นแล้วไม่ได้ โดยหนึ่งโมดูลต่อไฟล์จะตรงกันข้ามคือระบบบิลด์ มีความยืดหยุ่นสูงสุดในการแคชและกำหนดเวลาขั้นตอนการสร้างบิลด์ แต่ วิศวกรต้องใช้ความพยายามมากขึ้นในการรักษารายการทรัพยากร Dependency การเปลี่ยนไฟล์ที่จะอ้างอิง

แม้ว่าความละเอียดที่แน่นอนจะแตกต่างกันไปตามภาษา (และมักแตกต่างกันไปภายในภาษาเดียวกัน) แต่ Google มีแนวโน้มที่จะชอบโมดูลที่เล็กกว่ามากเมื่อเทียบกับที่เขียนในระบบบิลด์แบบอิงตามงาน ไบนารีเวอร์ชันที่ใช้งานจริงทั่วไปที่ Google มักอาศัยเป้าหมายหลายหมื่นรายการ และแม้กระทั่งเป้าหมายที่มีขนาดปานกลาง ทีมสามารถเป็นเจ้าของเป้าหมายหลายร้อยเป้าหมายภายในฐานของโค้ดได้ สำหรับภาษาอย่าง Java ที่มีแนวคิดการแพ็กเกจในตัวที่ชัดเจน แต่ละไดเรกทอรีมักจะมีแพ็กเกจ เป้าหมาย และไฟล์ BUILD ไฟล์เดียว (Pants ซึ่งเป็นระบบการสร้างอีกระบบหนึ่งที่ใช้ Bazel เรียกสิ่งนี้ว่ากฎ 1:1:1) ภาษาที่มีแพ็กเกจที่ไม่รัดกุม แบบแผนมักกำหนดเป้าหมายหลายรายการต่อ BUILD ไฟล์

ประโยชน์ของเป้าหมายการสร้างที่เล็กลงจะเริ่มแสดงผลเมื่อสร้างจำนวนมาก เนื่องจากจะทําให้การสร้างแบบกระจายเร็วขึ้นและลดความจำเป็นในการสร้างเป้าหมายใหม่ได้ ข้อดีจะชัดเจนยิ่งขึ้นเมื่อเริ่มการทดสอบ เนื่องจากเป้าหมายที่ละเอียดยิ่งขึ้นช่วยให้ระบบบิลด์ทำงานได้ชาญฉลาดมากขึ้นในการเรียกใช้ชุดทดสอบแบบจำกัดซึ่งอาจได้รับผลกระทบจากการเปลี่ยนแปลงหนึ่งๆ เนื่องจาก Google เชื่อในประโยชน์ที่เป็นระบบของการใช้ขนาดเล็กลง เราก็ได้สร้างความก้าวหน้าในการลดผลเสียโดยการลงทุนใน จัดการไฟล์ BUILD โดยอัตโนมัติเพื่อไม่ให้เป็นภาระกับนักพัฒนาซอฟต์แวร์

เครื่องมือเหล่านี้บางรายการ เช่น buildifier และ buildozer มีให้ใช้งานกับ Bazel ในไดเรกทอรี buildtools

การลดการเปิดเผยโมดูล

Bazel และระบบบิลด์อื่นๆ จะช่วยให้แต่ละเป้าหมายสามารถระบุการมองเห็น — ซึ่งกำหนดว่าเป้าหมายอื่นๆ ใดที่จะใช้ได้ เป้าหมายส่วนตัวจะอ้างอิงได้ภายในไฟล์ BUILD ของเป้าหมายนั้นเท่านั้น เป้าหมายอาจให้สิทธิ์เข้าถึงที่กว้างขึ้นแก่เป้าหมายของรายการไฟล์ BUILD ที่ระบุไว้อย่างชัดเจน หรือในกรณีที่มีสิทธิ์เข้าถึงแบบสาธารณะ เป้าหมายดังกล่าวจะให้สิทธิ์เข้าถึงแก่ทุกเป้าหมายในพื้นที่ทำงาน

เช่นเดียวกับภาษาโปรแกรมส่วนใหญ่ วิธีที่ดีที่สุดคือการลดการแสดงผลเนื่องจาก ให้ได้มากที่สุด โดยทั่วไปแล้ว ทีมใน Google จะเปิดเผยเป้าหมายต่อสาธารณะก็ต่อเมื่อเป้าหมายเหล่านั้นแสดงถึงไลบรารีที่ใช้กันอย่างแพร่หลายซึ่งพร้อมให้บริการแก่ทีมใดก็ได้ใน Google ทีมที่ต้องการให้ผู้อื่นประสานงานก่อนใช้โค้ดจะ คงรายการที่อนุญาตของลูกค้าเป้าหมายไว้เป็นระดับการเข้าถึงของกลุ่มเป้าหมาย เป้าหมายการติดตั้งใช้งานภายในของทีมแต่ละทีมจะจํากัดไว้เฉพาะไดเรกทอรีที่ทีมเป็นเจ้าของ และไฟล์ BUILD ส่วนใหญ่จะมีเป้าหมายเพียงรายการเดียวที่ไม่ใช่แบบส่วนตัว

การจัดการการขึ้นต่อกัน

โมดูลต้องอ้างอิงถึงกันได้ ข้อเสียของการทำลาย Codebase ต่างๆ ลงในโมดูลแบบละเอียดก็คือคุณต้องจัดการทรัพยากร Dependency ในโมดูลเหล่านั้น (แต่เครื่องมือจะช่วยให้การทำงานเป็นไปโดยอัตโนมัติได้) การแสดงออกถึงความเกี่ยวข้องเหล่านี้มักจะกลายเป็นเนื้อหาส่วนใหญ่ในไฟล์ BUILD

ทรัพยากร Dependency ภายใน

ในโปรเจ็กต์ขนาดใหญ่ที่แบ่งออกเป็นโมดูลแบบละเอียด ทรัพยากร Dependency ส่วนใหญ่ มักจะมาจากภายใน นั่นคือ ในเป้าหมายอื่นที่กำหนดไว้ และสร้างไว้ใน ที่เก็บซอร์ส Dependency ภายในแตกต่างจาก Dependency ภายนอกตรงที่ Dependency ภายในจะสร้างขึ้นจากซอร์สโค้ดแทนที่จะดาวน์โหลดเป็นอาร์ติแฟกต์ที่สร้างไว้ล่วงหน้าขณะเรียกใช้บิลด์ ซึ่งหมายความว่าจะไม่มีแนวคิด "เวอร์ชัน" สําหรับข้อกําหนดเบื้องต้นภายใน เป้าหมายและข้อกําหนดเบื้องต้นภายในทั้งหมดจะสร้างขึ้นจากคอมมิต/การแก้ไขเดียวกันในที่เก็บเสมอ ปัญหาหนึ่งที่ควรจะเป็น มีการจัดการอย่างรอบคอบเกี่ยวกับทรัพยากร Dependency ภายใน ทรัพยากร Dependency แบบสับเปลี่ยน (รูปที่ 1) สมมติว่าเป้าหมาย ก ขึ้นอยู่กับเป้าหมาย ข ซึ่งขึ้นอยู่กับเป้าหมาย ค ในไลบรารีทั่วไป เป้าหมาย ก ควรใช้คลาสที่กําหนดไว้ในเป้าหมาย ค ได้ไหม

Dependency แบบทรานซิทีฟ

รูปที่ 1 ทรัพยากร Dependency แบบทรานซิทีฟ

เครื่องมือพื้นฐานไม่ต้องกังวลในเรื่องนี้ ทั้งคู่ B และ C จะเชื่อมโยงกับเป้าหมาย A เมื่อสร้างแล้ว ดังนั้นสัญลักษณ์ใดๆ ที่กำหนดไว้ใน A รู้จัก Bazel ทำสิ่งนี้มานานหลายปี แต่เมื่อ Google เติบโตขึ้น เริ่มพบปัญหา สมมติว่า B ถูกเปลี่ยนโครงสร้างภายในโค้ดแล้ว ที่จำเป็นต่อการพึ่งพา C ถ้าทรัพยากร Dependency ของ B ถูกนําออกแล้ว A ก็มีการนำ A เป้าหมายที่ใช้ C ผ่านทรัพยากร Dependency บน B จะใช้งานไม่ได้ ในทางปฏิบัติแล้ว Dependency ของเป้าหมายจะกลายเป็นส่วนหนึ่งของสัญญาสาธารณะและไม่สามารถเปลี่ยนแปลงได้อย่างปลอดภัย ซึ่งหมายความว่าความเกี่ยวข้องจะเพิ่มขึ้นเรื่อยๆ เมื่อเวลาผ่านไป และการสร้างที่ Google ก็เริ่มช้าลง

ในที่สุด Google ก็แก้ปัญหานี้ได้ด้วยการเปิดตัว "โหมดการพึ่งพาแบบโยงอย่างเข้มงวด" ใน Bazel ในโหมดนี้ Bazel จะตรวจจับว่าเป้าหมายพยายามอ้างอิงสัญลักษณ์โดยไม่ใช้สัญลักษณ์นั้นโดยตรงหรือไม่ หากใช่ ระบบจะแสดงข้อผิดพลาดและคำสั่งเชลล์ที่ใช้แทรกข้อกำหนดโดยอัตโนมัติ นำการเปลี่ยนแปลงนี้ไปใช้ในฐานของโค้ดทั้งหมดของ Google และ การเปลี่ยนโครงสร้างภายในโค้ดเป้าหมายทุกรายการหลายล้านรายการ เพื่อแสดงรายการ ทรัพยากร Dependency ต้องใช้ความพยายามหลายปี แต่ก็คุ้มค่ามาก ตอนนี้บิลด์ของเราเร็วขึ้นมากเนื่องจากเป้าหมายมี Dependency ที่ไม่จำเป็นน้อยลง และวิศวกรสามารถนํา Dependency ที่ไม่จําเป็นออกได้โดยไม่ต้องกังวลว่าจะทําให้เป้าหมายที่ต้องใช้ Dependency เหล่านั้นใช้งานไม่ได้

และเช่นเคย การบังคับใช้ทรัพยากร Dependency แบบทรานซิชันที่เข้มงวดถือเป็นการทดแทนกัน ทำ สร้างไฟล์ที่มีรายละเอียดมากขึ้น เนื่องจากตอนนี้ไลบรารีที่ใช้บ่อยจำเป็นต้องแสดงไว้ในรายการ ในหลายที่ แทนที่จะดึงเข้ามาโดยไม่ตั้งใจ และวิศวกร ต้องใช้ความพยายามมากขึ้นในการเพิ่มทรัพยากร Dependency ลงในไฟล์ BUILD ตั้งแต่นั้นมา เราได้พัฒนาเครื่องมือที่ช่วยลดความยุ่งยากนี้ด้วยการตรวจหาข้อกำหนดเบื้องต้นที่ขาดหายไปจำนวนมากโดยอัตโนมัติและเพิ่มลงในไฟล์ BUILD โดยไม่ต้องมีการดำเนินการใดๆ จากนักพัฒนาซอฟต์แวร์ แต่ถึงแม้จะไม่มีเครื่องมือดังกล่าว เราพบว่าข้อเสียนั้นคุ้มค่าเมื่อโค้ดเบสมีขนาดใหญ่ขึ้น การเพิ่มการพึ่งพาในไฟล์ BUILD อย่างชัดแจ้งเป็นค่าใช้จ่ายแบบครั้งเดียว แต่การจัดการกับการพึ่งพาแบบทรานซิทีฟโดยนัยอาจทำให้เกิดปัญหาอย่างต่อเนื่องตราบใดที่มีเป้าหมายการสร้างอยู่ Bazel จะบังคับใช้ Dependency แบบทรานซิทีฟที่เข้มงวดในโค้ด Java โดยค่าเริ่มต้น

ทรัพยากร Dependency ภายนอก

หากความเกี่ยวข้องไม่ใช่ภายใน จะต้องเป็นภายนอก ทรัพยากร Dependency ภายนอกคือทรัพยากรในอาร์ติแฟกต์ที่สร้างขึ้นและจัดเก็บไว้นอกระบบบิลด์ ระบบจะนําเข้าข้อกําหนดโดยตรงจากที่เก็บอาร์ติแฟกต์ (โดยปกติจะเข้าถึงผ่านอินเทอร์เน็ต) และใช้ตามที่นําเข้ามาแทนที่จะสร้างจากแหล่งที่มา หนึ่งใน ความแตกต่างที่ยิ่งใหญ่ที่สุดระหว่างทรัพยากร Dependency ภายนอกและภายในก็คือ ทรัพยากร Dependency ภายนอกมีเวอร์ชันต่างๆ และเวอร์ชันเหล่านั้นมีอยู่อย่างอิสระจาก ซอร์สโค้ดของโครงการ

การจัดการทรัพยากร Dependency อัตโนมัติเทียบกับการจัดการทรัพยากรด้วยตนเอง

ระบบบิลด์อนุญาตให้จัดการเวอร์ชันของไลบรารีภายนอกได้ไม่ว่าจะด้วยตนเองหรือโดยอัตโนมัติ เมื่อจัดการด้วยตนเอง ไฟล์บิลด์จะแสดงเวอร์ชันที่ต้องการดาวน์โหลดจากที่เก็บอาร์ติแฟกต์อย่างชัดเจน โดยมักใช้สตริงเวอร์ชันเชิงความหมาย เช่น 1.1.4 เมื่อจัดการโดยอัตโนมัติ ไฟล์แหล่งที่มาจะระบุช่วงของ เวอร์ชันที่ยอมรับได้ และระบบบิลด์จะดาวน์โหลดเวอร์ชันล่าสุดเสมอ สำหรับ ตัวอย่างเช่น Gradle อนุญาตให้ประกาศเวอร์ชันทรัพยากร Dependency เป็น "1.+" เพื่อระบุ ทรัพยากร Dependency ย่อยหรือเวอร์ชันแพตช์ใดๆ ก็ยอมรับได้ตราบใดที่ เวอร์ชันหลักคือ 1

Dependency ที่มีการจัดการโดยอัตโนมัติอาจสะดวกสำหรับโปรเจ็กต์ขนาดเล็ก แต่มักเป็นสาเหตุของปัญหาในโปรเจ็กต์ขนาดใหญ่หรือโปรเจ็กต์ที่มีวิศวกรมากกว่า 1 คนดำเนินการ ปัญหาเกี่ยวกับ โดยอัตโนมัติ ทรัพยากร Dependency ที่จัดการคือ คุณไม่สามารถควบคุมได้ว่าเมื่อใดที่จะ อัปเดตแล้ว เราไม่อาจรับประกันได้ว่าบุคคลภายนอกจะไม่ทำการอัปเดตที่ทำให้เกิดข้อขัดข้อง (แม้ว่าจะอ้างว่าใช้การกําหนดเวอร์ชันแบบเชิงอรรถศาสตร์ก็ตาม) ดังนั้นบิลด์ที่ใช้งานได้ในวันหนึ่งอาจใช้งานไม่ได้ในวันถัดไปโดยที่ตรวจหาสิ่งที่เปลี่ยนแปลงหรือเปลี่ยนกลับไปเป็นสถานะที่ใช้งานได้นั้นทำได้ยาก แม้ว่าบิลด์จะไม่ขัดข้อง แต่ลักษณะการทำงานหรือการเปลี่ยนแปลงประสิทธิภาพเล็กๆ น้อยๆ ก็อาจเกิดขึ้นได้ ซึ่งจะติดตามได้ยาก

ในทางกลับกัน เนื่องจากทรัพยากร Dependency ที่จัดการด้วยตนเองจำเป็นต้องมีการเปลี่ยนแปลงในแหล่งที่มา ผู้ใช้สามารถค้นพบและย้อนกลับได้ง่ายๆ และยังมีโอกาส ลองดูที่เก็บเวอร์ชันเก่าเพื่อสร้างด้วยทรัพยากร Dependency ที่เก่ากว่า Bazel กำหนดให้ต้องระบุเวอร์ชันของทรัพยากร Dependency ทั้งหมดด้วยตนเอง ที่เวลา ระดับปานกลาง ค่าใช้จ่ายในการจัดการเวอร์ชันด้วยตนเองที่คุ้มค่า ความเสถียรที่มีให้

กฎหนึ่งเวอร์ชัน

โดยปกติแล้ว ไลบรารีเวอร์ชันต่างๆ จะแสดงด้วยอาร์ติแฟกต์ที่แตกต่างกัน ดังนั้นในทางทฤษฎีแล้ว ก็ไม่มีเหตุผลที่การประกาศทรัพยากรภายนอกเดียวกันในเวอร์ชันต่างๆ ในระบบการสร้างจะต้องใช้ชื่อเดียวกัน วิธีนี้ช่วยให้แต่ละเป้าหมายเลือกเวอร์ชันของ Dependency ที่ต้องการใช้ได้ ซึ่งทำให้เกิดปัญหาจำนวนมากในทางปฏิบัติ Google จึงบังคับใช้มาตรการ กฎเวอร์ชันเดียว สำหรับทรัพยากร Dependency ของบุคคลที่สามทั้งหมดในโค้ดเบสของเรา

ปัญหาใหญ่ที่สุดของการอนุญาตให้ใช้หลายเวอร์ชันคือปัญหาการพึ่งพาเพชร สมมติว่าเป้าหมาย A ขึ้นอยู่กับเป้าหมาย B และ v1 ของเป้าหมายภายนอก ไลบรารี หากเป้าหมาย B มีการเปลี่ยนโครงสร้างภายในโค้ดในภายหลังเพื่อเพิ่มทรัพยากร Dependency ใน v2 ของรายการเดียวกัน ไลบรารีภายนอก เป้าหมาย A จะใช้ไม่ได้ เนื่องจากตอนนี้ขึ้นอยู่กับ 2 สิ่งนี้โดยปริยาย เวอร์ชันต่างๆ ของไลบรารีเดียวกัน อย่างมีประสิทธิภาพ การใช้ ทรัพยากร Dependency ใหม่จากเป้าหมายไปยังไลบรารี ของบุคคลที่สามที่มีหลายเวอร์ชัน เนื่องจากผู้ใช้ของเป้าหมายเหล่านั้นอาจพึ่งพา เวอร์ชัน การปฏิบัติตามกฎเวอร์ชันเดียวจะทำให้ความขัดแย้งนี้ไม่เกิดขึ้น หากเป้าหมายเพิ่มการพึ่งพาไลบรารีของบุคคลที่สาม การพึ่งพาที่มีอยู่จะอยู่ในเวอร์ชันเดียวกันอยู่แล้ว ดังนั้นจึงอยู่ร่วมกันได้อย่างราบรื่น

ทรัพยากร Dependency ภายนอกแบบทรานซิทีฟ

การจัดการกับ Dependency แบบทรานซิทีฟของ Dependency ภายนอกอาจเป็นเรื่องยากเป็นพิเศษ ที่เก็บอาร์ติแฟกต์หลายรายการ เช่น Maven Central อนุญาต เพื่อระบุทรัพยากร Dependency ของอาร์ติแฟกต์อื่นๆ เวอร์ชันที่เฉพาะเจาะจงใน ที่เก็บได้ สร้างเครื่องมืออย่างเช่น Maven หรือ Gradle ที่ต้องทำการดาวน์โหลดซ้ำๆ การขึ้นต่อกันแบบทรานซิทีฟโดยค่าเริ่มต้น ซึ่งหมายความว่าการเพิ่มการขึ้นต่อกันเดี่ยวลงใน โปรเจ็กต์ของคุณอาจทำให้มีการดาวน์โหลดอาร์ติแฟกต์ ทั้งหมด

ซึ่งสะดวกมาก เมื่อเพิ่มการพึ่งพาในไลบรารีใหม่ คุณจะต้องติดตามการพึ่งพาแบบเปลี่ยนผ่านของไลบรารีแต่ละรายการและเพิ่มทั้งหมดด้วยตนเอง แต่ก็มีข้อเสียสำคัญอีกเช่นกัน เพราะ อาจขึ้นอยู่กับเวอร์ชันต่างๆ ของไลบรารีเดียวกันของบุคคลที่สาม จำเป็นต้องละเมิดกฎเวอร์ชันเดียวและนำไปสู่ไดมอนด์ ปัญหาการพึ่งพา หากเป้าหมายใช้ไลบรารีภายนอก 2 รายการที่ใช้ทรัพยากรเดียวกันในเวอร์ชันที่แตกต่างกัน คุณจะไม่สามารถคาดเดาได้ว่าจะได้รับเวอร์ชันใด ซึ่งหมายความว่าการอัปเดตทรัพยากร Dependency ภายนอกอาจดูเหมือน ความล้มเหลวที่ไม่เกี่ยวข้องทั่วทั้งฐานของโค้ดหากเวอร์ชันใหม่เริ่มดึงข้อมูล เวอร์ชันที่ขัดแย้งกันของทรัพยากร Dependency บางอย่าง

ด้วยเหตุนี้ Bazel จึงไม่ดาวน์โหลดทรัพยากร Dependency แบบทรานซิทีฟโดยอัตโนมัติ และขออภัยที่ยังไม่มีวิธีแก้ปัญหาที่ได้ผลแน่นอน ทางเลือกของ Bazel คือการใช้ไฟล์ส่วนกลางที่แสดงรายการทรัพยากรภายนอกทั้งหมดของที่เก็บและเวอร์ชันที่ชัดเจนที่ใช้สำหรับทรัพยากรนั้นทั่วทั้งที่เก็บ แต่โชคดีที่ Bazel มีเครื่องมือที่สามารถสร้างไฟล์ดังกล่าวซึ่งมีทรัพยากร Maven ชุดหนึ่งๆ ที่ต้องพึ่งพากันโดยอ้อมโดยอัตโนมัติ เครื่องมือนี้จะทำงานได้ 1 ครั้งเพื่อสร้างไฟล์ WORKSPACE เริ่มต้น สำหรับโปรเจ็กต์ แล้วระบบจะอัปเดตไฟล์ดังกล่าวด้วยตัวเองเพื่อปรับเวอร์ชัน ของทรัพยากร Dependency แต่ละรายการ

ขอย้ำอีกครั้งว่านี่เป็นตัวเลือกหนึ่งระหว่างความสะดวกและความสามารถในการปรับขนาด เล็ก โปรเจ็กต์อาจไม่ต้องกังวลเกี่ยวกับการจัดการทรัพยากร Dependency แบบทรานซิทีฟ และอาจหลีกเลี่ยงการใช้ทรานซิทีฟอัตโนมัติได้ ทรัพยากร Dependency กลยุทธ์นี้ดึงดูดใจน้อยลงเรื่อยๆ เมื่อองค์กรและโค้ดเบสเติบโตขึ้น และความขัดแย้งและผลลัพธ์ที่ไม่คาดคิดก็เกิดขึ้นบ่อยขึ้น สำหรับขนาดใหญ่ การจัดการทรัพยากร Dependency ด้วยตนเองนั้นมีค่าใช้จ่ายอย่างมาก ซึ่งน้อยกว่าค่าใช้จ่ายในการจัดการกับปัญหาที่เกิดจากทรัพยากร Dependency อัตโนมัติ การจัดการ

การแคชผลการบิลด์โดยใช้ทรัพยากรภายนอก

ทรัพยากร Dependency ภายนอกมักจะให้บริการโดยบุคคลที่สามที่ปล่อย เวอร์ชันเสถียรของไลบรารี อาจจะไม่มีการให้ซอร์สโค้ด องค์กรบางแห่งอาจเลือกทำให้โค้ดบางส่วนของตนเองพร้อมใช้งานเป็นอาร์ติแฟกต์ด้วย ซึ่งจะช่วยให้โค้ดส่วนอื่นๆ สามารถใช้โค้ดเหล่านั้นเป็นบุคคลที่สามแทนการพึ่งพาภายในได้ ซึ่งในทางทฤษฎีแล้วอาจช่วยเร่งเวลาสร้างได้หากอาร์ติแฟกต์ใช้เวลาสร้างนานแต่ดาวน์โหลดได้เร็ว

แต่วิธีนี้ก็ทำให้เกิดค่าใช้จ่ายและความซับซ้อนเพิ่มขึ้น กล่าวคือ ต้องมีคนทำงาน มีหน้าที่สร้างอาร์ติแฟกต์เหล่านี้ และอัปโหลดไปยัง ที่เก็บอาร์ติแฟกต์ และลูกค้าต้องดูแลให้พวกเขาติดตามข้อมูลล่าสุดอยู่เสมอ เวอร์ชันล่าสุด การแก้ไขข้อบกพร่องก็ยากขึ้นมากด้วย เนื่องจากระบบจะสร้างส่วนต่างๆ จากจุดต่างๆ ในที่เก็บ และจะไม่มีมุมมองที่สอดคล้องกันของซอร์สทรีอีกต่อไป

วิธีแก้ปัญหาที่ดีกว่าในการสร้างอาร์ติแฟกต์ที่ใช้เวลานานคือการใช้ระบบบิลด์ที่รองรับการแคชระยะไกลตามที่อธิบายไว้ก่อนหน้านี้ ระบบดังกล่าวจะบันทึกอาร์ติแฟกต์ที่ได้จากการบิลด์ทุกรายการไปยังตำแหน่งที่แชร์กับวิศวกรทุกคน ดังนั้นหากนักพัฒนาซอฟต์แวร์ใช้อาร์ติแฟกต์ที่ผู้อื่นเพิ่งสร้าง ระบบบิลด์จะดาวน์โหลดอาร์ติแฟกต์นั้นโดยอัตโนมัติแทนที่จะสร้างใหม่ วิธีนี้ให้ประโยชน์ด้านประสิทธิภาพทั้งหมดของการใช้อาร์ติแฟกต์โดยตรง ในขณะที่ยังคงทำให้บิลด์สอดคล้องกันราวกับว่าบิลด์มาจากแหล่งที่มาเดียวกันเสมอ นี่คือ ที่ Google ใช้ภายในและสามารถกำหนดค่า Bazel ให้ใช้รีโมต แคช

ความปลอดภัยและความน่าเชื่อถือของทรัพยากร Dependency ภายนอก

การใช้อาร์ติแฟกต์จากแหล่งที่มาของบุคคลที่สามมีความเสี่ยงโดยเนื้อแท้ แหล่งที่มาของบุคคลที่สาม (เช่น ที่เก็บอาร์ติแฟกต์) อาจหยุดทำงานได้ ซึ่งจะทำให้การบิลด์ทั้งหมดหยุดชะงักหากดาวน์โหลดทรัพยากรภายนอกไม่ได้ และยังมีความเสี่ยงด้านความปลอดภัยด้วย เช่น หากระบบของบุคคลที่สาม ถูกโจมตีโดยผู้โจมตี ผู้โจมตีสามารถแทนที่ ด้วยดีไซน์ของตัวเอง ซึ่งช่วยให้ใส่โค้ดที่กำหนดเองได้ ในงานสร้างของคุณ คุณลดปัญหาทั้ง 2 ข้อได้ด้วยการมิเรอร์อาร์ติแฟกต์ที่คุณ ขึ้นอยู่กับเซิร์ฟเวอร์ที่คุณควบคุมและบล็อกไม่ให้ระบบบิลด์ของคุณเข้าถึง ที่เก็บอาร์ติแฟกต์ของบุคคลที่สาม เช่น Maven Central ข้อเสียคือ การดูแลรักษามิเรอร์เหล่านี้ต้องใช้ความพยายามและทรัพยากร ดังนั้น การเลือกว่าจะใช้หรือไม่จึงมักขึ้นอยู่กับขนาดของโปรเจ็กต์ นอกจากนี้ คุณยังป้องกันปัญหาด้านความปลอดภัยได้ทั้งหมดโดยแทบไม่สิ้นเปลืองทรัพยากรใดๆ โดยกำหนดให้ระบุแฮชของอาร์ติแฟกต์ของบุคคลที่สามแต่ละรายการในที่เก็บข้อมูลต้นทาง ซึ่งจะทำให้การบิลด์ล้มเหลวหากมีการดัดแปลงอาร์ติแฟกต์ อีกทางเลือกหนึ่งที่ ทำให้ปัญหาคือผู้ให้บริการทรัพยากร Dependency ของโปรเจ็กต์ เมื่อโปรเจ็กต์ ของผู้ให้บริการทรัพยากร Dependency ได้ ผู้ให้บริการจะตรวจสอบ การควบคุมแหล่งที่มาควบคู่ไปกับ ซอร์สโค้ดของโปรเจ็กต์ ไม่ว่าจะเป็นต้นฉบับหรือเป็นไบนารี ซึ่งหมายความว่าระบบจะแปลงการพึ่งพาภายนอกทั้งหมดของโปรเจ็กต์เป็นการพึ่งพาภายใน Google ใช้แนวทางนี้ภายใน โดยตรวจสอบไลบรารีของบุคคลที่สามทั้งหมดที่อ้างอิงทั่วทั้ง Google ลงในไดเรกทอรี third_party ที่รูทของต้นไม้ซอร์สโค้ดของ Google อย่างไรก็ตาม วิธีนี้ได้ผลที่ Google เพียงเพราะว่า ระบบควบคุมแหล่งที่มานั้นสร้างขึ้นเอง เพื่อรองรับการผูกขาดที่มีขนาดใหญ่มาก ผู้ให้บริการอาจไม่ใช่ตัวเลือกสำหรับองค์กรบางแห่ง