การจัดการทรัพยากร Dependency

รายงานปัญหา ดูแหล่งที่มา

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

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

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

การใช้โมดูลแบบละเอียดและกฎ 1:1:1

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

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

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

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

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

ย่อระดับการเข้าถึงโมดูล

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

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

การจัดการทรัพยากร Dependency

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

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

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

ทรัพยากร Dependency ชั่วคราว

รูปที่ 1 ทรัพยากร Dependency ชั่วคราว

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

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

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

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

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

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

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

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

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

กฎเวอร์ชันเดียว

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

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

ทรัพยากร Dependency ชั่วคราวทางอ้อม

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

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

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

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

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

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

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

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

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

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