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

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

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

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

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

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

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

โครงการส่วนใหญ่จะอยู่ระหว่างปลายสุดเหล่านี้ และการเลือกนี้จะต้องมี ข้อดีข้อเสียระหว่างประสิทธิภาพและความสามารถในการบำรุงรักษา การใช้โมดูลเดียวสำหรับฟังก์ชัน ทั้งโปรเจ็กต์อาจหมายความว่าคุณไม่ต้องแตะไฟล์ 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 ในโมดูลเหล่านั้น (แต่เครื่องมือจะช่วยให้การทำงานเป็นไปโดยอัตโนมัติได้) แสดงสิ่งเหล่านี้ ทรัพยากร Dependency มักกลายเป็นเนื้อหาจำนวนมากในไฟล์ BUILD

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

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

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

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

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

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

และเช่นเคย การบังคับใช้ทรัพยากร Dependency แบบทรานซิชันที่เข้มงวดถือเป็นการทดแทนกัน ทำ สร้างไฟล์ที่มีรายละเอียดมากขึ้น เนื่องจากตอนนี้ไลบรารีที่ใช้บ่อยจำเป็นต้องแสดงไว้ในรายการ ในหลายที่ แทนที่จะดึงเข้ามาโดยไม่ตั้งใจ และวิศวกร ต้องใช้ความพยายามมากขึ้นในการเพิ่มทรัพยากร Dependency ลงในไฟล์ BUILD เราได้ดำเนินการตั้งแต่ เครื่องมือที่ได้รับการพัฒนา ซึ่งช่วยลดงานหนักด้วยการตรวจหา ทรัพยากร Dependency และการเพิ่มลงในไฟล์ BUILD โดยไม่มีนักพัฒนาซอฟต์แวร์ การฝึกฝนด้วยตนเอง แต่ถึงจะไม่มีเครื่องมือเหล่านี้ เราก็พบข้อเสียคือ คุ้มค่ากับการปรับขนาดของโค้ดเบส: การเพิ่มทรัพยากร Dependency ให้กับไฟล์ BUILD อย่างชัดเจน เป็นค่าใช้จ่ายครั้งเดียว แต่การจัดการกับทรัพยากร Dependency แบบโดยนัยอาจทำให้เกิด ปัญหาที่มีอย่างต่อเนื่องตราบใดที่ยังมีเป้าหมายของบิลด์อยู่ บาเซล บังคับใช้ทรัพยากร 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 ทั้งหมดด้วยตนเอง ที่เวลา ระดับปานกลาง ค่าใช้จ่ายในการจัดการเวอร์ชันด้วยตนเองที่คุ้มค่า ความเสถียรที่มีให้

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

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

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

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

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

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

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

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

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

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

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

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

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

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