เมื่อดูหน้าก่อนๆ จะเห็นว่ามีธีมหนึ่งที่ปรากฏซ้ำๆ นั่นคือการจัดการโค้ดของคุณเองนั้นค่อนข้างตรงไปตรงมา แต่การจัดการการอ้างอิงของโค้ดนั้นยากกว่ามาก การพึ่งพามีหลายรูปแบบ บางครั้งก็เป็นการพึ่งพางาน (เช่น "ส่งเอกสารก่อนที่จะทำเครื่องหมายว่ารุ่นเสร็จสมบูรณ์") และบางครั้งก็เป็นการพึ่งพาอาร์ติแฟกต์ (เช่น "ฉันต้องมีไลบรารี Computer Vision เวอร์ชันล่าสุดเพื่อสร้างโค้ด") บางครั้งคุณอาจมีทรัพยากร Dependency ภายในในโค้ดเบสส่วนอื่น และ บางครั้งคุณอาจมีทรัพยากร Dependency ภายนอกในโค้ดหรือข้อมูลที่ทีมอื่นเป็นเจ้าของ (ไม่ว่าจะเป็นในองค์กรของคุณหรือบุคคลที่สาม) แต่ไม่ว่าในกรณีใด แนวคิดที่ว่า "ฉันต้องมีสิ่งนั้นก่อนจึงจะมีสิ่งนี้ได้" เป็นสิ่งที่เกิดขึ้นซ้ำๆ ในการออกแบบระบบบิลด์ และการจัดการการขึ้นต่อกันอาจเป็นงานพื้นฐานที่สุดของระบบบิลด์
การจัดการโมดูลและทรัพยากร Dependency
โปรเจ็กต์ที่ใช้ระบบบิลด์ที่อิงตามอาร์ติแฟกต์ เช่น Bazel จะแบ่งออกเป็นชุด
ของโมดูล โดยโมดูลจะแสดงการอ้างอิงซึ่งกันและกันผ่านไฟล์ BUILD
การจัดระเบียบโมดูลและการขึ้นต่อกันอย่างเหมาะสมจะส่งผลอย่างมากต่อทั้งประสิทธิภาพของระบบบิลด์และปริมาณงานที่ต้องใช้ในการบำรุงรักษา
การใช้โมดูลแบบละเอียดและกฎ 1:1:1
คำถามแรกที่เกิดขึ้นเมื่อสร้างโครงสร้างการสร้างที่อิงตามอาร์ติแฟกต์คือ
การตัดสินใจว่าโมดูลแต่ละโมดูลควรมีฟังก์ชันการทำงานมากน้อยเพียงใด ใน Bazel
โมดูลจะแสดงด้วยเป้าหมายที่ระบุหน่วยที่สร้างได้ เช่น java_library หรือ go_binary ในกรณีที่ซับซ้อนที่สุด โปรเจ็กต์ทั้งหมดอาจอยู่ในโมดูลเดียวโดยวางไฟล์ BUILD ไว้ที่รูทและ
รวบรวมไฟล์ต้นฉบับทั้งหมดของโปรเจ็กต์นั้นแบบเรียกซ้ำ ในอีกด้านหนึ่ง
ไฟล์ต้นฉบับเกือบทุกไฟล์สามารถสร้างเป็นโมดูลของตัวเองได้ ซึ่งจะทำให้
ต้องระบุไฟล์อื่นๆ ที่ขึ้นอยู่กับไฟล์นั้นในไฟล์ BUILD
โปรเจ็กต์ส่วนใหญ่จะอยู่ระหว่าง 2 ขั้วนี้ และการเลือกจะเกี่ยวข้องกับ
การแลกเปลี่ยนระหว่างประสิทธิภาพและความสามารถในการบำรุงรักษา การใช้โมดูลเดียวสำหรับทั้งโปรเจ็กต์อาจหมายความว่าคุณไม่จำเป็นต้องแตะไฟล์ BUILD เลย ยกเว้นเมื่อเพิ่มการอ้างอิงภายนอก แต่ก็หมายความว่าระบบบิลด์ต้องบิลด์ทั้งโปรเจ็กต์พร้อมกันเสมอ ซึ่งหมายความว่าเครื่องมือจะไม่สามารถ
ขนานหรือกระจายชิ้นส่วนของการสร้าง และจะไม่สามารถแคชชิ้นส่วน
ที่สร้างแล้วได้ ส่วนการใช้โมดูลเดียวต่อไฟล์นั้นตรงกันข้าม ระบบบิลด์จะมีความยืดหยุ่นสูงสุดในการแคชและกำหนดเวลาขั้นตอนการบิลด์ แต่วิศวกรจะต้องใช้ความพยายามมากขึ้นในการดูแลรายการการอ้างอิงทุกครั้งที่มีการเปลี่ยนแปลงไฟล์ที่อ้างอิง
แม้ว่าระดับความละเอียดที่แน่นอนจะแตกต่างกันไปตามภาษา (และมักจะแตกต่างกันแม้ในภาษาเดียวกัน) แต่ Google มักจะชอบโมดูลที่มีขนาดเล็กกว่ามากเมื่อเทียบกับโมดูลที่มักจะเขียนในระบบบิลด์ตามงาน โดยปกติแล้วไบนารีการผลิตที่ Google มักจะขึ้นอยู่กับเป้าหมายหลายหมื่นรายการ และแม้แต่ทีมขนาดปานกลางก็อาจมีเป้าหมายหลายร้อยรายการภายในฐานของโค้ด สำหรับภาษาอย่าง Java ซึ่งมีแนวคิดเรื่องการแพ็กเกจในตัวที่ชัดเจน โดยปกติแล้วแต่ละไดเรกทอรีจะมีแพ็กเกจ เป้าหมาย และไฟล์ BUILD เพียงรายการเดียว (Pants ซึ่งเป็นระบบบิลด์อีกระบบหนึ่งที่อิงตาม Bazel เรียกกฎนี้ว่ากฎ 1:1:1) ภาษาที่มีรูปแบบการจัดแพ็กเกจที่อ่อนแอกว่า
มักจะกำหนดเป้าหมายหลายรายการต่อไฟล์ BUILD
ประโยชน์ของเป้าหมายการสร้างขนาดเล็กจะเริ่มเห็นผลเมื่อมีการปรับขนาด เนื่องจากจะทำให้การสร้างแบบกระจายเร็วขึ้นและลดความจำเป็นในการสร้างเป้าหมายใหม่
ข้อดีจะยิ่งน่าสนใจมากขึ้นหลังจากที่การทดสอบเข้ามามีบทบาท เนื่องจาก
เป้าหมายที่ละเอียดยิ่งขึ้นหมายความว่าระบบบิลด์จะฉลาดขึ้นมากในการ
เรียกใช้เฉพาะชุดย่อยของการทดสอบที่จำกัดซึ่งอาจได้รับผลกระทบจากการเปลี่ยนแปลงที่กำหนด
เนื่องจาก Google เชื่อว่าการใช้เป้าหมายที่เล็กลงจะช่วยให้เกิดประโยชน์ในระบบ เราจึงได้พยายามลดข้อเสียด้วยการลงทุนในเครื่องมือเพื่อจัดการไฟล์ BUILD โดยอัตโนมัติเพื่อไม่ให้นักพัฒนาแอปต้องรับภาระ
เครื่องมือบางอย่าง เช่น buildifier และ buildozer จะพร้อมใช้งานกับ Bazel ในไดเรกทอรี buildtools
การลดระดับการเข้าถึงโมดูล
Bazel และระบบบิลด์อื่นๆ อนุญาตให้แต่ละเป้าหมายระบุระดับการเข้าถึง ซึ่งเป็นพร็อพเพอร์ตี้ที่กำหนดว่าเป้าหมายอื่นๆ ใดบ้างที่อาจขึ้นอยู่กับเป้าหมายนั้น ออบเจ็กต์เป้าหมายส่วนตัว
จะอ้างอิงได้ภายในBUILDของตัวเองเท่านั้น เป้าหมายอาจให้สิทธิ์การมองเห็นที่กว้างขึ้น
แก่เป้าหมายของรายการไฟล์ BUILD ที่กำหนดไว้อย่างชัดเจน หรือในกรณีที่มีการมองเห็นแบบสาธารณะ ก็จะให้สิทธิ์แก่ทุกเป้าหมายในพื้นที่ทำงาน
เช่นเดียวกับภาษาโปรแกรมส่วนใหญ่ โดยปกติแล้วควรลดระดับการมองเห็นให้ได้มากที่สุด
โดยทั่วไปแล้ว ทีมที่ Google จะเผยแพร่เป้าหมายต่อสาธารณะก็ต่อเมื่อ
เป้าหมายเหล่านั้นแสดงถึงไลบรารีที่ใช้กันอย่างแพร่หลายซึ่งพร้อมให้ทุกทีมที่ Google ใช้งาน
ทีมที่กำหนดให้ผู้อื่นประสานงานกับทีมก่อนใช้โค้ดจะ
ดูแลรายการที่อนุญาตของเป้าหมายลูกค้าในฐานะระดับการมองเห็นของเป้าหมาย การติดตั้งใช้งานภายในของแต่ละทีมจะจำกัดไว้เฉพาะไดเรกทอรีที่ทีมเป็นเจ้าของเท่านั้น และไฟล์ BUILD ส่วนใหญ่จะมีเป้าหมายเพียงรายการเดียวที่ไม่ใช่แบบส่วนตัว
การจัดการทรัพยากร Dependency
โมดูลต้องอ้างอิงถึงกันได้ ข้อเสียของการแบ่งโค้ดเบสออกเป็นโมดูลแบบละเอียดคือคุณต้องจัดการการอ้างอิงระหว่างโมดูลเหล่านั้น (แม้ว่าเครื่องมือจะช่วยทำให้กระบวนการนี้เป็นแบบอัตโนมัติได้) โดยปกติแล้ว การแสดงการอ้างอิงเหล่านี้จะเป็นเนื้อหาส่วนใหญ่ในไฟล์ BUILD
ทรัพยากร Dependency ภายใน
ในโปรเจ็กต์ขนาดใหญ่ที่แบ่งออกเป็นโมดูลแบบละเอียด การอ้างอิงส่วนใหญ่ มักจะเป็นการอ้างอิงภายใน นั่นคือการอ้างอิงเป้าหมายอื่นที่กำหนดและสร้างในที่เก็บแหล่งข้อมูลเดียวกัน Dependency ภายในแตกต่างจาก Dependency ภายนอกตรงที่สร้างจากแหล่งที่มาแทนที่จะดาวน์โหลดเป็นอาร์ติแฟกต์ที่สร้างไว้ล่วงหน้า ขณะเรียกใช้บิลด์ ซึ่งหมายความว่าไม่มีแนวคิดเรื่อง "เวอร์ชัน" สำหรับ การอ้างอิงภายใน โดยเป้าหมายและการอ้างอิงภายในทั้งหมดจะ สร้างขึ้นที่คอมมิต/การแก้ไขเดียวกันในที่เก็บเสมอ ปัญหาหนึ่งที่ควร จัดการอย่างระมัดระวังเกี่ยวกับ Dependency ภายในคือวิธีจัดการกับ Dependency แบบทรานซิทีฟ (รูปที่ 1) สมมติว่าเป้าหมาย A ขึ้นอยู่กับเป้าหมาย B ซึ่งขึ้นอยู่กับเป้าหมายไลบรารีทั่วไป C เป้าหมาย A ควรใช้คลาสที่กำหนดไว้ในเป้าหมาย C ได้ไหม
รูปที่ 1 ทรัพยากร Dependency แบบทรานซิทีฟ
เครื่องมือพื้นฐานไม่มีปัญหาใดๆ กับการดำเนินการนี้ เนื่องจากทั้ง B และ C จะลิงก์ไปยังเป้าหมาย A เมื่อสร้างขึ้น ดังนั้น A จะรู้จักสัญลักษณ์ใดๆ ที่กำหนดไว้ใน C Bazel อนุญาตให้ทำเช่นนี้มาหลายปีแล้ว แต่เมื่อ Google เติบโตขึ้น เราก็เริ่มเห็นปัญหา สมมติว่ามีการปรับโครงสร้าง B เพื่อให้ไม่จำเป็นต้อง ขึ้นอยู่กับ C อีกต่อไป หากมีการนำการขึ้นต่อกันของ B กับ C ออก A และเป้าหมายอื่นๆ ที่ใช้ C ผ่านการขึ้นต่อกันของ B จะใช้งานไม่ได้ กล่าวคือ การอ้างอิงของเป้าหมาย กลายเป็นส่วนหนึ่งของสัญญาแบบสาธารณะและไม่สามารถเปลี่ยนแปลงได้อย่างปลอดภัย ซึ่งหมายความว่าการพึ่งพาจะสะสมไปเรื่อยๆ และการสร้างที่ Google เริ่มช้าลง
ในที่สุด Google ก็แก้ไขปัญหานี้ได้ด้วยการเปิดตัว "โหมดการอ้างอิงแบบเข้มงวด" ใน Bazel ในโหมดนี้ Bazel จะตรวจหาว่าเป้าหมายพยายามอ้างอิงสัญลักษณ์โดยไม่ได้ขึ้นอยู่กับสัญลักษณ์นั้นโดยตรงหรือไม่ หากใช่ Bazel จะล้มเหลวพร้อมข้อผิดพลาดและคำสั่งเชลล์ที่ใช้เพื่อแทรกการอ้างอิงโดยอัตโนมัติได้ การเปิดตัวการเปลี่ยนแปลงนี้ในโค้ดเบสทั้งหมดของ Google และ การปรับโครงสร้างเป้าหมายการสร้างหลายล้านรายการของเราแต่ละรายการเพื่อแสดงรายการ การอ้างอิงอย่างชัดเจนเป็นความพยายามที่ใช้เวลาหลายปี แต่ก็คุ้มค่า ตอนนี้การสร้างของเราเร็วขึ้นมากเนื่องจากเป้าหมายมีทรัพยากร Dependency ที่ไม่จำเป็นน้อยลง และวิศวกรก็มีสิทธิ์นำทรัพยากร Dependency ที่ไม่จำเป็นออกได้โดยไม่ต้องกังวลว่าจะทำให้เป้าหมายที่ขึ้นอยู่กับทรัพยากรเหล่านั้นเสียหาย
ตามปกติแล้ว การบังคับใช้การขึ้นต่อกันแบบทรานซิทีฟที่เข้มงวดต้องมีการแลกเปลี่ยนข้อดีข้อเสีย ซึ่งทำให้ไฟล์บิลด์มีคำอธิบายมากขึ้น เนื่องจากตอนนี้ต้องระบุไลบรารีที่ใช้บ่อยอย่างชัดเจนในหลายที่แทนที่จะดึงมาโดยบังเอิญ และวิศวกรต้องใช้ความพยายามมากขึ้นในการเพิ่มการขึ้นต่อกันในไฟล์ BUILD นับตั้งแต่นั้นมา เราได้
พัฒนาเครื่องมือที่ช่วยลดภาระนี้ด้วยการตรวจหาการอ้างอิงที่ขาดหายไปจำนวนมากโดยอัตโนมัติ
และเพิ่มการอ้างอิงเหล่านั้นลงในไฟล์ BUILD โดยไม่ต้องมี
การแทรกแซงจากนักพัฒนาแอป แต่แม้จะไม่มีเครื่องมือดังกล่าว เราก็พบว่าการแลกเปลี่ยนนี้คุ้มค่าเมื่อฐานโค้ดขยายขนาด การเพิ่มการอ้างอิงอย่างชัดเจนไปยังไฟล์ BUILD เป็นค่าใช้จ่ายแบบครั้งเดียว แต่การจัดการกับการอ้างอิงแบบทรานซิทีฟโดยนัยอาจทำให้เกิดปัญหาต่อเนื่องตราบใดที่เป้าหมายการสร้างยังคงอยู่ โดยค่าเริ่มต้น Bazel จะบังคับใช้การขึ้นต่อกันแบบ
ทรานซิทีฟ
ที่เข้มงวด
กับโค้ด Java
ทรัพยากร Dependency ภายนอก
หากการอ้างอิงไม่ได้อยู่ภายใน ก็ต้องอยู่ภายนอก Dependency ภายนอกคือ Dependency ที่มีต่ออาร์ติแฟกต์ที่สร้างและจัดเก็บไว้นอกระบบบิลด์ ระบบจะนำเข้า Dependency โดยตรงจากที่เก็บที่สร้างขึ้น (โดยปกติจะเข้าถึงผ่านอินเทอร์เน็ต) และใช้ตามที่เป็นอยู่แทนที่จะสร้างจากแหล่งที่มา ความแตกต่างที่สำคัญอย่างหนึ่งระหว่างการอ้างอิงภายนอกและการอ้างอิงภายในคือการอ้างอิงภายนอกจะมีเวอร์ชัน และเวอร์ชันเหล่านั้นจะแยกจากซอร์สโค้ดของโปรเจ็กต์
การจัดการทรัพยากร Dependency โดยอัตโนมัติเทียบกับการจัดการด้วยตนเอง
ระบบบิลด์สามารถอนุญาตให้จัดการเวอร์ชันของทรัพยากร Dependency ภายนอกได้
ด้วยตนเองหรือโดยอัตโนมัติ เมื่อจัดการด้วยตนเอง ไฟล์บิลด์จะแสดงรายการเวอร์ชันที่ต้องการดาวน์โหลดจากที่เก็บอาร์ติแฟกต์อย่างชัดเจน โดยมักใช้สตริงเวอร์ชันเชิงความหมาย เช่น 1.1.4 เมื่อมีการจัดการโดยอัตโนมัติ ไฟล์ต้นฉบับจะระบุช่วงของเวอร์ชันที่ยอมรับได้ และระบบบิลด์จะดาวน์โหลดเวอร์ชันล่าสุดเสมอ ตัวอย่างเช่น Gradle อนุญาตให้ประกาศเวอร์ชันของ Dependency เป็น "1.+" เพื่อระบุว่ายอมรับ Dependency เวอร์ชันย่อยหรือเวอร์ชันแพตช์ใดก็ได้ ตราบใดที่เวอร์ชันหลักเป็น 1
การขึ้นต่อกันที่มีการจัดการโดยอัตโนมัติอาจสะดวกสำหรับโปรเจ็กต์ขนาดเล็ก แต่โดยปกติแล้ว มักเป็นสูตรที่ทำให้เกิดปัญหาในโปรเจ็กต์ที่มีขนาดใหญ่พอสมควรหรือโปรเจ็กต์ที่ มีวิศวกรมากกว่า 1 คนทำงานร่วมกัน ปัญหาของการขึ้นต่อกันที่จัดการโดยอัตโนมัติคือคุณควบคุมไม่ได้ว่าจะอัปเดตเวอร์ชันเมื่อใด ไม่มีวิธีใดรับประกันได้ว่าบุคคลภายนอกจะไม่ทำการอัปเดตที่ทำให้เกิดการเปลี่ยนแปลงที่ไม่เข้ากัน (แม้ว่าบุคคลภายนอกจะอ้างว่าใช้การกำหนดเวอร์ชันเชิงความหมายก็ตาม) ดังนั้นบิลด์ที่ทำงานได้ในวันหนึ่งอาจใช้งานไม่ได้ในวันถัดไป และไม่มีวิธีง่ายๆ ในการตรวจหาว่ามีการเปลี่ยนแปลงใดเกิดขึ้น หรือย้อนกลับไปเป็นสถานะที่ใช้งานได้ แม้ว่าบิลด์จะไม่เสีย แต่ก็อาจมีการเปลี่ยนแปลงลักษณะการทำงานหรือประสิทธิภาพเล็กๆ น้อยๆ ที่ติดตามไม่ได้
ในทางตรงกันข้าม เนื่องจากทรัพยากร Dependency ที่จัดการด้วยตนเองต้องมีการเปลี่ยนแปลงในระบบควบคุมแหล่งที่มา จึงค้นพบและย้อนกลับได้ง่าย และคุณสามารถเช็คเอาต์ที่เก็บเวอร์ชันเก่าเพื่อสร้างด้วยทรัพยากร Dependency เก่าได้ Bazel กำหนดให้ต้องระบุเวอร์ชันของทรัพยากร Dependency ทั้งหมดด้วยตนเอง แม้ในระดับปานกลาง ค่าใช้จ่ายในการจัดการเวอร์ชันด้วยตนเองก็คุ้มค่ากับความเสถียรที่ได้รับ
กฎเวอร์ชันเดียว
โดยปกติแล้วไลบรารีเวอร์ชันต่างๆ จะแสดงด้วยอาร์ติแฟกต์ที่แตกต่างกัน ดังนั้นในทางทฤษฎีแล้วจึงไม่มีเหตุผลที่เวอร์ชันต่างๆ ของการอ้างอิงภายนอกเดียวกัน จะประกาศทั้ง 2 เวอร์ชันในระบบบิลด์ภายใต้ชื่อที่แตกต่างกันไม่ได้ ด้วยวิธีนี้ เป้าหมายแต่ละรายการจะเลือกเวอร์ชันของทรัพยากร Dependency ที่ต้องการใช้ได้ ซึ่งทำให้เกิดปัญหามากมายในทางปฏิบัติ ดังนั้น Google จึงบังคับใช้กฎเวอร์ชันเดียวอย่างเคร่งครัดกับ การขึ้นต่อกันของบุคคลที่สามทั้งหมดในโค้ดเบสของเรา
ปัญหาที่ใหญ่ที่สุดของการอนุญาตให้ใช้หลายเวอร์ชันคือปัญหาการขึ้นต่อกันแบบไดมอนด์ สมมติว่าเป้าหมาย A ขึ้นอยู่กับเป้าหมาย B และไลบรารีภายนอกเวอร์ชัน 1 หากต่อมามีการปรับโครงสร้างเป้าหมาย B เพื่อเพิ่มการอ้างอิงใน v2 ของไลบรารีภายนอกเดียวกัน เป้าหมาย A จะใช้งานไม่ได้เนื่องจากตอนนี้ขึ้นอยู่กับไลบรารีเดียวกัน 2 เวอร์ชันโดยนัย ในทางปฏิบัติ การเพิ่มการอ้างอิงใหม่จากเป้าหมายไปยังไลบรารีของบุคคลที่สามที่มีหลายเวอร์ชันไม่เคยปลอดภัย เนื่องจากผู้ใช้เป้าหมายรายใดรายหนึ่งอาจอ้างอิงเวอร์ชันอื่นอยู่แล้ว การปฏิบัติตามกฎเวอร์ชันเดียวจะทำให้ความขัดแย้งนี้เป็นไปไม่ได้ หากเป้าหมายเพิ่มการอ้างอิงในไลบรารีของบุคคลที่สาม การอ้างอิงที่มีอยู่จะอยู่ในเวอร์ชันเดียวกันอยู่แล้ว จึงทำงานร่วมกันได้อย่างราบรื่น
การอ้างอิงภายนอกแบบทรานซิทีฟ
การจัดการกับทรัพยากร Dependency แบบทรานซิทีฟของทรัพยากร Dependency ภายนอกอาจเป็นเรื่องยากเป็นพิเศษ ที่เก็บอาร์ติแฟกต์หลายแห่ง เช่น Maven Central อนุญาตให้อาร์ติแฟกต์ระบุทรัพยากร Dependency ในอาร์ติแฟกต์อื่นๆ บางเวอร์ชันในที่เก็บ เครื่องมือบิลด์ เช่น Maven หรือ Gradle มักจะดาวน์โหลดทรัพยากร Dependency แบบทรานซิทีฟแต่ละรายการแบบเรียกซ้ำโดยค่าเริ่มต้น ซึ่งหมายความว่าการเพิ่มทรัพยากร Dependency รายการเดียวในโปรเจ็กต์อาจทำให้มีการดาวน์โหลดอาร์ติแฟกต์หลายสิบรายการโดยรวม
ซึ่งสะดวกมาก เพราะเมื่อเพิ่มการอ้างอิงในไลบรารีใหม่ การติดตามการอ้างอิงแบบทรานซิทีฟของไลบรารีแต่ละรายการ และเพิ่มการอ้างอิงทั้งหมดด้วยตนเองจะเป็นเรื่องที่น่าปวดหัวมาก แต่ก็มีข้อเสียอย่างมากเช่นกัน เนื่องจากไลบรารีต่างๆ อาจขึ้นอยู่กับไลบรารีของบุคคลที่สามเดียวกันในเวอร์ชันที่แตกต่างกัน กลยุทธ์นี้จึงละเมิดกฎเวอร์ชันเดียวอย่างหลีกเลี่ยงไม่ได้และทำให้เกิดปัญหาการขึ้นต่อกันแบบไดมอนด์ หากเป้าหมายของคุณขึ้นอยู่กับไลบรารีภายนอก 2 รายการที่ใช้ การพึ่งพาเดียวกันแต่เป็นเวอร์ชันที่แตกต่างกัน คุณจะ ไม่สามารถบอกได้ว่าจะได้รับเวอร์ชันใด ซึ่งหมายความว่าการอัปเดตทรัพยากร Dependency ภายนอกอาจทำให้เกิดข้อผิดพลาดที่ไม่เกี่ยวข้องในโค้ดเบสทั้งหมด หากเวอร์ชันใหม่เริ่มดึงเวอร์ชันที่ขัดแย้งของทรัพยากร Dependency บางรายการ
Bazel ไม่ได้ใช้เพื่อดาวน์โหลดการขึ้นต่อกันแบบทรานซิทีฟโดยอัตโนมัติ ก่อนหน้านี้จะใช้ไฟล์ WORKSPACE ซึ่งกำหนดให้ต้องแสดงรายการทรัพยากร Dependency แบบทรานซิทีฟทั้งหมด ซึ่งทำให้เกิดความยุ่งยากเป็นอย่างมากเมื่อจัดการทรัพยากร Dependency ภายนอก นับตั้งแต่นั้นมา Bazel
ได้เพิ่มการรองรับการจัดการการอ้างอิงภายนอกแบบทรานซิทีฟอัตโนมัติ
ในรูปแบบของไฟล์ MODULE.bazel ดูรายละเอียดเพิ่มเติมได้ที่ภาพรวมของ
การขึ้นต่อภายนอก
อีกครั้งที่ตัวเลือกในที่นี้คือความสะดวกและความสามารถในการปรับขนาด โปรเจ็กต์ขนาดเล็กอาจไม่ต้องการกังวลเรื่องการจัดการการอ้างอิงแบบทรานซิทีฟด้วยตนเอง และอาจใช้การอ้างอิงแบบทรานซิทีฟอัตโนมัติได้ กลยุทธ์นี้จะน่าสนใจน้อยลงเรื่อยๆ เมื่อองค์กรและโค้ดเบสเติบโตขึ้น และความขัดแย้งและผลลัพธ์ที่ไม่คาดคิดจะเกิดขึ้นบ่อยขึ้นเรื่อยๆ ในระดับที่ใหญ่ขึ้น ค่าใช้จ่ายในการจัดการการขึ้นต่อกันด้วยตนเองจะน้อยกว่าค่าใช้จ่ายในการจัดการปัญหาที่เกิดจากการจัดการการขึ้นต่อกันโดยอัตโนมัติมาก
แคชผลลัพธ์การสร้างโดยใช้ทรัพยากร Dependency ภายนอก
โดยส่วนใหญ่แล้ว การขึ้นต่อกันภายนอกมักมาจากบุคคลที่สามที่เผยแพร่ไลบรารีเวอร์ชันที่เสถียร โดยอาจไม่มีซอร์สโค้ด นอกจากนี้ องค์กรบางแห่งอาจเลือกที่จะทำให้โค้ดบางส่วนของตนเองพร้อมใช้งานเป็นอาร์ติแฟกต์ ซึ่งจะช่วยให้โค้ดส่วนอื่นๆ ขึ้นอยู่กับโค้ดดังกล่าวเป็นทรัพยากรของบุคคลที่สามแทนที่จะเป็นทรัพยากรภายใน ในทางทฤษฎีแล้ว วิธีนี้จะช่วยเร่งความเร็วการบิลด์ได้หากสิ่งประดิษฐ์ บิลด์ช้าแต่ดาวน์โหลดเร็ว
อย่างไรก็ตาม วิธีนี้ยังทำให้เกิดค่าใช้จ่ายและความซับซ้อนมากมายด้วย กล่าวคือ ต้องมีคนรับผิดชอบในการสร้างอาร์ติแฟกต์แต่ละรายการและอัปโหลดไปยังที่เก็บอาร์ติแฟกต์ และไคลเอ็นต์ต้องตรวจสอบว่าอาร์ติแฟกต์เป็นเวอร์ชันล่าสุดอยู่เสมอ การแก้ไขข้อบกพร่องก็จะยากขึ้นมากเช่นกัน เนื่องจากส่วนต่างๆ ของระบบสร้างขึ้นจากจุดต่างๆ ในที่เก็บ และไม่มีมุมมองที่สอดคล้องกันของโครงสร้างแหล่งที่มาอีกต่อไป
วิธีที่ดีกว่าในการแก้ปัญหาอาร์ติแฟกต์ที่ใช้เวลานานในการสร้างคือการ ใช้ระบบบิลด์ที่รองรับการแคชระยะไกล ดังที่อธิบายไว้ก่อนหน้านี้ ระบบบิลด์ดังกล่าวจะบันทึกอาร์ติแฟกต์ที่ได้จากการบิลด์ทุกครั้งไปยังตำแหน่งที่ แชร์กันในหมู่วิศวกร ดังนั้นหากนักพัฒนาซอฟต์แวร์ต้องใช้อาร์ติแฟกต์ที่ เพิ่งสร้างโดยบุคคลอื่น ระบบบิลด์จะดาวน์โหลดอาร์ติแฟกต์นั้นโดยอัตโนมัติ แทนที่จะสร้างขึ้นมาใหม่ ซึ่งจะให้ประโยชน์ด้านประสิทธิภาพทั้งหมดของ การอิงตามอาร์ติแฟกต์โดยตรง ในขณะเดียวกันก็ยังคงมั่นใจได้ว่าบิลด์จะ สอดคล้องกันราวกับว่าสร้างจากแหล่งที่มาเดียวกันเสมอ นี่คือ กลยุทธ์ที่ Google ใช้ภายใน และกำหนดค่า Bazel ให้ใช้แคชระยะไกลได้
ความปลอดภัยและความน่าเชื่อถือของทรัพยากร Dependency ภายนอก
การใช้สิ่งประดิษฐ์จากแหล่งที่มาของบุคคลที่สามมีความเสี่ยงโดยธรรมชาติ มีความเสี่ยงด้านความพร้อมใช้งานหากแหล่งที่มาของบุคคลที่สาม (เช่น ที่เก็บที่สร้างขึ้น)
หยุดทำงาน เนื่องจากบิลด์ทั้งหมดอาจหยุดชะงักหากดาวน์โหลด
การอ้างอิงภายนอกไม่ได้ นอกจากนี้ยังมีความเสี่ยงด้านความปลอดภัยด้วย หากผู้โจมตีบุกรุกระบบของบุคคลที่สาม ผู้โจมตีจะสามารถแทนที่อาร์ติแฟกต์ที่อ้างอิงด้วยอาร์ติแฟกต์ที่ออกแบบเอง ซึ่งจะทำให้ผู้โจมตีแทรกโค้ดที่กำหนดเองลงในการสร้างของคุณได้ คุณสามารถลดปัญหาทั้ง 2 อย่างได้โดยการมิเรอร์อาร์ติแฟกต์ที่คุณใช้ไปยังเซิร์ฟเวอร์ที่คุณควบคุม และบล็อกไม่ให้ระบบบิลด์เข้าถึงที่เก็บอาร์ติแฟกต์ของบุคคลที่สาม เช่น Maven Central
ข้อเสียคือการดูแลรักษามิเรอร์เหล่านี้ต้องใช้ความพยายามและทรัพยากร ดังนั้น
การเลือกว่าจะใช้หรือไม่จึงขึ้นอยู่กับขนาดของโปรเจ็กต์
นอกจากนี้ คุณยังป้องกันปัญหาด้านความปลอดภัยได้อย่างสมบูรณ์โดยมีค่าใช้จ่ายเพียงเล็กน้อยด้วยการ
กำหนดให้ระบุแฮชของอาร์ติแฟกต์ของบุคคลที่สามแต่ละรายการในที่เก็บแหล่งที่มา
ซึ่งจะทำให้บิลด์ล้มเหลวหากมีการดัดแปลงอาร์ติแฟกต์ อีกทางเลือกหนึ่งที่หลีกเลี่ยงปัญหานี้ได้โดยสิ้นเชิงคือการจัดหาทรัพยากร Dependency ของโปรเจ็กต์จากผู้ให้บริการ เมื่อโปรเจ็กต์จัดจำหน่ายทรัพยากร Dependency ของตนเอง โปรเจ็กต์จะตรวจสอบทรัพยากรเหล่านั้นใน
การควบคุมแหล่งที่มาควบคู่ไปกับซอร์สโค้ดของโปรเจ็กต์ ไม่ว่าจะเป็นในรูปแบบซอร์สหรือไบนารี
ซึ่งหมายความว่าการขึ้นต่อกันภายนอกทั้งหมดของโปรเจ็กต์
จะเปลี่ยนเป็นการขึ้นต่อกันภายใน Google ใช้วิธีนี้ภายในองค์กร
โดยจะตรวจสอบไลบรารีของบุคคลที่สามทุกรายการที่อ้างอิงทั่วทั้ง Google แล้วย้ายไปยังไดเรกทอรี third_party
ที่รูทของโครงสร้างแหล่งข้อมูลของ Google อย่างไรก็ตาม วิธีนี้ใช้ได้ที่ Google เท่านั้น เนื่องจากระบบควบคุมแหล่งที่มาของ Google สร้างขึ้นมาโดยเฉพาะเพื่อจัดการ
Monorepo ขนาดใหญ่มาก ดังนั้นการใช้ Vendor อาจไม่ใช่ตัวเลือกสำหรับทุกองค์กร
