ระบบบิลด์ที่อิงตามอาร์ติแฟกต์

รายงานปัญหา ดูซอร์สโค้ด รุ่น Nightly · 8.0 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

ระบบบิลด์ที่อิงตามอาร์ติแฟกต์มีงานจำนวนไม่มากนักที่ระบบกำหนดไว้ ซึ่งวิศวกรสามารถกําหนดค่าได้ในแบบจํากัด วิศวกรยังคงบอกระบบว่าต้องสร้างอะไร แต่ระบบการสร้างจะกำหนดวิธีสร้าง ระบบการบิลด์ที่อิงตามอาร์ติแฟกต์ เช่น Bazel ยังคงมีไฟล์บิลด์เช่นเดียวกับระบบการบิลด์ที่อิงตามงาน แต่เนื้อหาของไฟล์บิลด์เหล่านั้นจะแตกต่างกันมาก ไฟล์บิลด์ใน Bazel ไม่ใช่ชุดคำสั่งแบบบังคับในภาษาสคริปต์แบบ Turing-complete ที่อธิบายวิธีสร้างเอาต์พุต แต่เป็นไฟล์ Manifest แบบประกาศที่อธิบายชุดอาร์ติแฟกต์ที่จะสร้าง ไลบรารีที่ใช้ร่วมกัน และชุดตัวเลือกแบบจำกัดที่ส่งผลต่อวิธีสร้าง เมื่อวิศวกรเรียกใช้ bazel ในบรรทัดคำสั่ง จะมีการระบุชุดเป้าหมายที่จะสร้าง (สิ่งที่ทำ) และ Bazel จะรับผิดชอบในการกำหนดค่า เรียกใช้ และกำหนดเวลาขั้นตอนการสร้าง (วิธีทำ) เนื่องจากตอนนี้ระบบบิลด์มีการควบคุมเครื่องมือที่จะเรียกใช้เมื่อใดอย่างเต็มรูปแบบ จึงรับประกันได้มากขึ้นซึ่งช่วยให้มีประสิทธิภาพมากขึ้นมาก ทั้งยังรับประกันความถูกต้องได้

มุมมองด้านฟังก์ชันการทำงาน

การเปรียบเทียบระบบบิลด์ที่อิงตามอาร์ติแฟกต์กับการเขียนโปรแกรมเชิงฟังก์ชันนั้นทําได้ง่าย ภาษาโปรแกรมแบบบังคับดั้งเดิม (เช่น Java, C และ Python) จะระบุรายการคำสั่งที่จะดำเนินการทีละรายการ ในลักษณะเดียวกับที่ระบบบิลด์ตามงานช่วยให้โปรแกรมเมอร์กำหนดชุดขั้นตอนที่จะดำเนินการ ในทางตรงกันข้าม ภาษาโปรแกรมเชิงฟังก์ชัน (เช่น Haskell และ ML) จะมีโครงสร้างคล้ายกับชุดสมการทางคณิตศาสตร์มากกว่า ในภาษาเชิงฟังก์ชัน นักเขียนโปรแกรมจะอธิบายการประมวลผลที่จะดำเนินการ แต่ปล่อยให้คอมไพเลอร์เป็นผู้กำหนดรายละเอียดเกี่ยวกับเวลาและวิธีดำเนินการประมวลผล

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

ทำความเข้าใจระบบการสร้างที่อิงตามอาร์ติแฟกต์

Blaze ซึ่งเป็นระบบบิลด์ของ Google เป็นระบบบิลด์ที่อิงตามอาร์ติแฟกต์ระบบแรก Bazel เป็น Blaze เวอร์ชันโอเพนซอร์ส

ไฟล์บิลด์ (ปกติจะตั้งชื่อว่า BUILD) ใน Bazel จะมีลักษณะดังนี้

java_binary(
    name = "MyBinary",
    srcs = ["MyBinary.java"],
    deps = [
        ":mylib",
    ],
)
java_library(
    name = "mylib",
    srcs = ["MyLibrary.java", "MyHelper.java"],
    visibility = ["//java/com/example/myproduct:__subpackages__"],
    deps = [
        "//java/com/example/common",
        "//java/com/example/myproduct/otherlib",
    ],
)

ใน Bazel ไฟล์ BUILD จะกำหนดเป้าหมาย ซึ่งเป้าหมาย 2 ประเภทในที่นี่คือ java_binary และ java_library เป้าหมายทุกรายการจะสอดคล้องกับอาร์ติแฟกต์ที่ระบบสร้างขึ้น เป้าหมายไบนารีจะสร้างไบนารีที่เรียกใช้ได้โดยตรง และเป้าหมายไลบรารีจะสร้างไลบรารีที่ไบนารีหรือไลบรารีอื่นๆ สามารถใช้ได้ เป้าหมายทุกรายการมีข้อมูลต่อไปนี้

  • name: วิธีที่ระบบอ้างอิงเป้าหมายในบรรทัดคำสั่งและเป้าหมายอื่นๆ
  • srcs: ไฟล์ต้นฉบับที่จะคอมไพล์เพื่อสร้างอาร์ติแฟกต์สําหรับเป้าหมาย
  • deps: เป้าหมายอื่นๆ ที่ต้องสร้างก่อนเป้าหมายนี้และลิงก์กับเป้าหมายนี้

Dependency จะอยู่ภายในแพ็กเกจเดียวกัน (เช่น Dependency ของ MyBinary ที่มีต่อ :mylib) หรืออยู่ในแพ็กเกจอื่นในลําดับชั้นแหล่งที่มาเดียวกัน (เช่น Dependency ของ mylib ที่มีต่อ //java/com/example/common)

คุณจะทำการบิลด์โดยใช้เครื่องมือบรรทัดคำสั่งของ Bazel เช่นเดียวกับระบบบิลด์แบบอิงตามงาน หากต้องการสร้างเป้าหมาย MyBinary ให้เรียกใช้ bazel build :MyBinary หลังจากป้อนคำสั่งดังกล่าวเป็นครั้งแรกในที่เก็บข้อมูลที่สะอาด Bazel จะดำเนินการต่อไปนี้

  1. แยกวิเคราะห์ไฟล์ BUILD ทั้งหมดในพื้นที่ทํางานเพื่อสร้างกราฟของความสัมพันธ์ระหว่างสิ่งต่างๆ
  2. ใช้กราฟเพื่อระบุการพึ่งพาแบบทรานซิทีฟของ MyBinary กล่าวคือ เป้าหมายทั้งหมดที่ MyBinary ต้องใช้และเป้าหมายทั้งหมดที่เป้าหมายเหล่านั้นต้องใช้แบบซ้ำ
  3. สร้างการอ้างอิงแต่ละรายการตามลําดับ Bazel จะเริ่มด้วยการสร้างแต่ละเป้าหมายที่ไม่มีทรัพยากรอื่นๆ และติดตามทรัพยากรที่ยังคงต้องสร้างสำหรับแต่ละเป้าหมาย ทันทีที่สร้างทรัพยากร Dependency ทั้งหมดของเป้าหมาย Bazel จะเริ่มสร้างเป้าหมายนั้น กระบวนการนี้จะดำเนินต่อไปจนกว่าจะมีการสร้างขึ้นทั้งหมดสำหรับ MyBinary ที่เป็น transitive dependency
  4. สร้าง MyBinary เพื่อสร้างไบนารีที่เรียกใช้งานได้ขั้นสุดท้ายซึ่งลิงก์กับข้อกำหนดทั้งหมดที่สร้างไว้ในขั้นตอนที่ 3

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

แต่ประโยชน์นั้นไม่ได้จำกัดอยู่แค่การทำงานแบบขนาน สิ่งที่เห็นได้ชัดอีกอย่างหนึ่งจากแนวทางนี้คือเมื่อนักพัฒนาแอปพิมพ์ bazel build :MyBinary ครั้งที่สองโดยไม่มีการเปลี่ยนแปลงใดๆ Bazel จะออกในพริบตาพร้อมข้อความว่าเป้าหมายเป็นเวอร์ชันล่าสุด ซึ่งเป็นไปได้เนื่องจากแพรามิติกการเขียนโปรแกรมแบบฟังก์ชันที่เราได้พูดถึงก่อนหน้านี้ Bazel รู้ว่าแต่ละเป้าหมายเป็นผลลัพธ์ของการเรียกใช้คอมไพเลอร์ Java เท่านั้น และรู้ว่าเอาต์พุตจากคอมไพเลอร์ Java นั้นขึ้นอยู่กับอินพุตเท่านั้น ดังนั้นตราบใดที่อินพุตไม่เปลี่ยนแปลง ก็สามารถนำเอาต์พุตมาใช้ซ้ำได้ และการวิเคราะห์นี้จะทำงานในทุกระดับ หาก MyBinary.java มีการเปลี่ยนแปลง Bazel จะรู้ว่าต้องสร้าง MyBinary ขึ้นมาใหม่ แต่ใช้ mylib ซ้ำ หากไฟล์ต้นฉบับของ //java/com/example/common มีการเปลี่ยนแปลง Bazel จะรู้ว่าต้องสร้างไลบรารี //java/com/example/common, mylib และ MyBinary ขึ้นมาใหม่ แต่ใช้ //java/com/example/myproduct/otherlib ซ้ำ เนื่องจาก Bazel ทราบเกี่ยวกับพร็อพเพอร์ตี้ของเครื่องมือที่ทำงานในทุกขั้นตอน จึงสามารถสร้างอาร์ติแฟกต์ชุดน้อยที่สุดได้ทุกครั้ง พร้อมกับรับประกันว่าจะไม่สร้างบิลด์ที่ล้าสมัย

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

เคล็ดลับอื่นๆ ของ Bazel

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

เครื่องมือที่เป็นทรัพยากร Dependency

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

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

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

  • การกำหนดค่าโฮสต์: เครื่องมือสร้างที่ทำงานระหว่างการสร้าง
  • การกำหนดค่าเป้าหมาย: การสร้างไบนารีที่คุณขอในท้ายที่สุด

การขยายระบบบิลด์

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

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

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

การแยกสภาพแวดล้อม

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

การทําให้ทรัพยากร Dependency ภายนอกเป็นแบบกำหนดได้

แต่ยังคงมีปัญหาอีกอย่างหนึ่งอยู่ ซึ่งก็คือระบบบิลด์มักจะต้องดาวน์โหลดข้อกำหนดเบื้องต้น (ไม่ว่าจะเป็นเครื่องมือหรือไลบรารี) จากแหล่งที่มาภายนอกแทนที่จะสร้างโดยตรง ซึ่งจะเห็นได้ในตัวอย่างผ่าน Dependency @com_google_common_guava_guava//jar ซึ่งจะดาวน์โหลดไฟล์ JAR จาก Maven

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

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

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

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

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