Quản lý phần phụ thuộc

Báo cáo vấn đề Xem nguồn Hằng đêm · 7,3 · 7.2 · 7.1 · 7 · 6,5

Khi xem qua các trang trước, chúng ta cứ đi đi tìm lại một chủ đề: quản lý mã của riêng bạn khá đơn giản, nhưng việc quản lý các phần phụ thuộc khó hơn. Có tất cả các loại phần phụ thuộc: đôi khi cũng có sự phụ thuộc vào một nhiệm vụ (chẳng hạn như "đẩy tài liệu trước khi tôi đánh dấu một bản phát hành là hoàn thành), và đôi khi có một phần phụ thuộc vào một cấu phần phần mềm (chẳng hạn như "Tôi cần có phiên bản mới nhất của thư viện thị giác máy tính để tạo mã của tôi"). Đôi khi, bạn có các phần phụ thuộc nội bộ trên một phần khác của cơ sở mã và đôi khi bạn có các phần phụ thuộc bên ngoài vào mã hoặc dữ liệu do một nhóm khác sở hữu (trong tổ chức của bạn hoặc bên thứ ba). Nhưng trong mọi trường hợp, ý tưởng về "tôi trước khi tôi có thể có được điều này" là điều gì đó lặp đi lặp lại nhiều lần trong thiết kế hệ thống xây dựng và quản lý các phần phụ thuộc có lẽ là cách hiệu quả nhất công việc cơ bản của một hệ thống xây dựng.

Xử lý các Mô-đun và phần phụ thuộc

Các dự án sử dụng các hệ thống xây dựng dựa trên cấu phần phần mềm như Bazel được chia thành một tập hợp của các mô-đun, trong đó các mô-đun biểu thị các phần phụ thuộc lẫn nhau thông qua BUILD tệp. Việc sắp xếp đúng cách những mô-đun và phần phụ thuộc này có thể gây ra rủi ro rất lớn ảnh hưởng đến cả hiệu suất của hệ thống xây dựng cũng như lượng công việc cần thiết duy trì.

Sử dụng các mô-đun chi tiết và quy tắc 1:1:1

Câu hỏi đầu tiên xuất hiện khi định cấu trúc một bản dựng dựa trên cấu phần phần mềm là quyết định mức độ chức năng mà một mô-đun riêng lẻ sẽ bao gồm. Ở Bazel, một mô-đun được biểu thị bằng một mục tiêu chỉ định một đơn vị có thể xây dựng như java_library hoặc go_binary. Ở một mức độ nào đó, toàn bộ dự án có thể được chứa trong một mô-đun đơn lẻ bằng cách đặt một tệp BUILD ở thư mục gốc và kết hợp đệ quy tất cả các tệp nguồn của dự án đó. Tại bên kia cực kỳ hiệu quả, gần như mọi tệp nguồn đều có thể được tạo thành mô-đun riêng, hiệu quả yêu cầu từng tệp phải liệt kê trong một tệp BUILD đối với mọi tệp khác mà tệp đó phụ thuộc.

Hầu hết các dự án đều nằm ở một mức nào đó giữa các cực trị này và việc lựa chọn liên quan đến sự đánh đổi giữa hiệu suất và khả năng bảo trì. Việc sử dụng một mô-đun duy nhất cho toàn bộ dự án có thể có nghĩa là bạn không bao giờ cần chạm vào tệp BUILD ngoại trừ khi thêm phần phụ thuộc bên ngoài, nhưng điều đó có nghĩa là hệ thống xây dựng phải tạo toàn bộ dự án cùng một lúc. Điều này có nghĩa là ứng dụng sẽ không thể tải song song hoặc phân phối các phần của bản dựng, đồng thời không thể lưu các phần vào bộ nhớ đệm mà nó đã được xây dựng. Mỗi tệp một mô-đun thì ngược lại: hệ thống xây dựng có sự linh hoạt tối đa trong việc lưu vào bộ nhớ đệm và lên lịch các bước của bản dựng, nhưng các kỹ sư cần nỗ lực hơn nữa trong việc duy trì danh sách các phần phụ thuộc bất cứ khi nào chúng sẽ thay đổi tệp nào tham chiếu đến tệp nào.

Mặc dù mức độ chi tiết chính xác thay đổi tuỳ theo ngôn ngữ (và thường ngay cả trong ngôn ngữ), Google có xu hướng ưu tiên các mô-đun nhỏ hơn đáng kể so với một mô-đun có thể thường ghi trong hệ thống xây dựng dựa trên nhiệm vụ. Một tệp nhị phân sản xuất điển hình tại Google thường phụ thuộc vào hàng chục nghìn mục tiêu, và thậm chí cả một có thể sở hữu hàng trăm mục tiêu trong cơ sở mã của mình. Đối với các ngôn ngữ như Java có khái niệm đóng gói tích hợp mạnh mẽ, mỗi thư mục thường chứa một gói, mục tiêu và tệp BUILD (Pants, một hệ thống xây dựng khác) dựa trên Bazel, gọi đây là quy tắc 1:1:1). Ngôn ngữ có hình thức trình bày yếu hơn quy ước thường xác định nhiều mục tiêu cho mỗi tệp BUILD.

Lợi ích của các mục tiêu bản dựng nhỏ hơn thực sự bắt đầu thể hiện trên quy mô lớn vì dẫn đến các bản dựng được phân phối nhanh hơn và nhu cầu tạo lại mục tiêu ít thường xuyên hơn. Các lợi thế trở nên hấp dẫn hơn sau khi thử nghiệm bước vào bức tranh, vì các mục tiêu chi tiết hơn có nghĩa là hệ thống xây dựng có thể hoạt động hiệu quả hơn nhiều chỉ chạy một tập hợp con thử nghiệm giới hạn có thể bị ảnh hưởng bởi bất kỳ thử nghiệm cụ thể nào thay đổi. Bởi vì Google tin vào các lợi ích mang tính hệ thống của việc sử dụng mục tiêu, chúng tôi đã có một số bước tiến trong việc giảm thiểu các nhược điểm bằng cách đầu tư vào để tự động quản lý các tệp BUILD nhằm tránh tạo gánh nặng cho nhà phát triển.

Một số công cụ trong số này, chẳng hạn như buildifierbuildozer, tương thích với Bazel ở Thư mục buildtools.

Giảm thiểu khả năng hiển thị của mô-đun

Bazel và các hệ thống xây dựng khác cho phép mỗi mục tiêu chỉ định một chế độ hiển thị — một để xác định các mục tiêu khác có thể phụ thuộc vào thuộc tính đó. Mục tiêu riêng tư chỉ có thể được tham chiếu trong tệp BUILD của chính nó. Mục tiêu có thể mở rộng phạm vi khả năng hiển thị các mục tiêu của một danh sách tệp BUILD được xác định rõ ràng hoặc trong cho trường hợp hiển thị công khai, cho mọi mục tiêu trong không gian làm việc.

Như với hầu hết ngôn ngữ lập trình, cách tốt nhất là giảm thiểu khả năng hiển thị như nhiều nhất có thể. Thông thường, các nhóm tại Google sẽ chỉ công khai mục tiêu nếu những mục tiêu đó đại diện cho các thư viện được sử dụng rộng rãi mà mọi đội ngũ tại Google đều có thể sử dụng. Các nhóm yêu cầu người khác phối hợp với họ trước khi sử dụng mã của họ sẽ duy trì danh sách cho phép các mục tiêu khách hàng làm chế độ hiển thị của mục tiêu. Một mục tiêu triển khai nội bộ của nhóm sẽ chỉ được giới hạn ở các thư mục do nhóm này sở hữu và hầu hết BUILD tệp sẽ chỉ có một mục tiêu không phải riêng tư.

Quản lý phần phụ thuộc

Các mô-đun phải có khả năng tham chiếu lẫn nhau. Nhược điểm của việc phá vỡ cơ sở mã vào các mô-đun chi tiết là bạn cần quản lý các phần phụ thuộc trong số các mô-đun đó (mặc dù các công cụ có thể giúp tự động hoá việc này). Thể hiện những điều này các phần phụ thuộc thường trở thành phần lớn nội dung trong tệp BUILD.

Phần phụ thuộc nội bộ

Trong một dự án lớn được chia thành các mô-đun chi tiết, hầu hết các phần phụ thuộc đều có khả năng là nội bộ; tức là trên một mục tiêu khác được xác định và xây dựng trong cùng một kho lưu trữ nguồn. Các phần phụ thuộc nội bộ khác với các phần phụ thuộc bên ngoài ở chúng được tạo từ nguồn thay vì được tải xuống dưới dạng cấu phần phần mềm tạo sẵn trong khi chạy bản dựng. Điều này cũng có nghĩa là không có khái niệm "phiên bản" cho phần phụ thuộc nội bộ – một mục tiêu và tất cả các phần phụ thuộc nội bộ của mục tiêu đó luôn là được tạo ở cùng một lần xác nhận/sửa đổi trong kho lưu trữ. Một vấn đề cần được được xử lý cẩn thận với các phần phụ thuộc nội bộ là cách xử lý phần phụ thuộc bắc cầu (Hình 1). Giả sử mục tiêu A phụ thuộc vào mục tiêu B, việc này phụ thuộc vào mục tiêu thư viện chung C. Phải nhắm đến A có thể sử dụng các lớp được xác định trong mục tiêu C?

Phần phụ thuộc bắc cầu

Hình 1 Phần phụ thuộc bắc cầu

Liên quan đến các công cụ cơ bản, việc này không có vấn đề gì; cả hai B và C sẽ được liên kết với mục tiêu A khi mục tiêu đó được tạo, do đó, mọi ký hiệu được xác định trong A biết đến C. Bazel đã cho phép điều này trong nhiều năm, nhưng khi Google phát triển, chúng tôi bắt đầu gặp phải vấn đề. Giả sử B được tái cấu trúc để không còn cần phụ thuộc vào C. Nếu sau đó sự phụ thuộc của B vào C bị xoá thì A và bất kỳ yếu tố nào khác mục tiêu đã sử dụng C thông qua phần phụ thuộc vào B sẽ bị lỗi. Một cách hiệu quả, các phần phụ thuộc đã trở thành một phần trong hợp đồng công khai và không bao giờ có thể an toàn đã thay đổi. Tức là các phần phụ thuộc được tích luỹ theo thời gian và được tạo dựng tại Google bắt đầu chậm lại.

Cuối cùng, Google đã giải quyết vấn đề này bằng cách giới thiệu "phương thức bắc cầu nghiêm ngặt chế độ phụ thuộc" trong Bazel. Ở chế độ này, Bazel phát hiện xem mục tiêu có cố gắng tham chiếu một ký hiệu mà không phụ thuộc trực tiếp vào biểu tượng đó và nếu có thì không thành công với và lệnh shell có thể dùng để tự động chèn phần phụ thuộc. Triển khai thay đổi này trên toàn bộ cơ sở mã của Google và tái cấu trúc từng mục tiêu trong số hàng triệu mục tiêu xây dựng để liệt kê rõ ràng phần phụ thuộc là nỗ lực nhiều năm, nhưng rất xứng đáng. Các công trình của chúng tôi nhanh hơn nhiều vì các mục tiêu có ít phần phụ thuộc không cần thiết hơn và các kỹ sư có quyền gỡ bỏ những phần phụ thuộc không cần thiết mà không phải lo lắng về các mục tiêu phá vỡ phụ thuộc vào chúng.

Như thường lệ, việc thực thi các phần phụ thuộc bắc cầu nghiêm ngặt sẽ đem lại sự đánh đổi. Đã thực hiện tệp bản dựng chi tiết hơn, vì các thư viện thường dùng hiện cần được liệt kê một cách rõ ràng ở nhiều nơi thay vì bị vô tình kéo vào, và các kỹ sư cần tốn nhiều công sức hơn để thêm phần phụ thuộc vào tệp BUILD. Kể từ đó, được phát triển nhằm giảm bớt khối lượng công việc này bằng cách tự động phát hiện nhiều công cụ bị thiếu phần phụ thuộc và thêm chúng vào tệp BUILD mà không cần bất kỳ nhà phát triển nào can thiệp. Nhưng ngay cả khi không có các công cụ như vậy, chúng tôi nhận thấy sự đánh đổi sẽ có lợi đáng giá nó khi cơ sở mã điều chỉnh theo tỷ lệ: thêm một phần phụ thuộc một cách rõ ràng vào tệp BUILD là chi phí một lần, nhưng việc xử lý các phần phụ thuộc bắc cầu ngầm có thể gây ra các sự cố đang diễn ra miễn là mục tiêu bản dựng tồn tại. Bazel sản xuất thực thi các phần phụ thuộc bắc cầu nghiêm ngặt trên mã Java theo mặc định.

Phần phụ thuộc bên ngoài

Nếu một phần phụ thuộc không phải là nội bộ thì đó phải là phần phụ thuộc bên ngoài. Các phần phụ thuộc bên ngoài là các mã trên các cấu phần phần mềm được tạo và lưu trữ bên ngoài hệ thống xây dựng. Chiến lược phát hành đĩa đơn phần phụ thuộc được nhập trực tiếp từ kho lưu trữ cấu phần phần mềm (thường được truy cập qua Internet) và được sử dụng nguyên trạng thay vì được tạo từ nguồn. Một trong số điểm khác biệt lớn nhất giữa các phần phụ thuộc bên ngoài và bên trong là các phần phụ thuộc bên ngoài có phiên bản và các phiên bản đó tồn tại độc lập với mã nguồn của dự án.

Quản lý phần phụ thuộc tự động so với thủ công

Hệ thống xây dựng có thể cho phép quản lý phiên bản của các phần phụ thuộc bên ngoài theo cách thủ công hoặc tự động. Khi được quản lý theo cách thủ công, tệp bản dựng liệt kê rõ phiên bản mà ứng dụng muốn tải xuống từ kho lưu trữ cấu phần phần mềm, thường sử dụng chuỗi phiên bản ngữ nghĩa, chẳng hạn như dưới tên 1.1.4. Khi được quản lý tự động, tệp nguồn chỉ định một phạm vi các phiên bản có thể chấp nhận được và hệ thống xây dựng luôn tải phiên bản mới nhất xuống. Để ví dụ: Gradle cho phép khai báo phiên bản phần phụ thuộc là “1.+” để chỉ định mọi phiên bản nhỏ hoặc bản vá của một phần phụ thuộc đều được chấp nhận, miễn là phiên bản lớn là 1.

Các phần phụ thuộc được quản lý tự động có thể thuận tiện cho các dự án nhỏ, nhưng chúng thường là công thức gây ra thảm hoạ đối với các dự án có quy mô không nhỏ hoặc do nhiều kỹ sư đảm nhiệm. Sự cố với phần phụ thuộc được quản lý là bạn không có quyền kiểm soát thời điểm phiên bản đã cập nhật. Không có cách nào để đảm bảo rằng các bên ngoài sẽ không gây lỗi (ngay cả khi chúng tuyên bố sử dụng phiên bản ngữ nghĩa), vì vậy, một bản dựng hoạt động tốt, ngày hôm sau có thể bị hỏng ngày tiếp theo mà không có cách nào dễ dàng để phát hiện nội dung nào đã thay đổi hoặc để khôi phục về trạng thái hoạt động. Ngay cả khi công trình không bị hỏng, có thể là những thay đổi nhỏ về hành vi hoặc hiệu suất mà bạn không thể theo dõi được.

Ngược lại, vì các phần phụ thuộc được quản lý theo cách thủ công nên bạn phải thay đổi nguồn quyền kiểm soát, họ có thể dễ dàng được khám phá và hoàn nguyên, cũng như có thể hãy xem phiên bản kho lưu trữ cũ để xây dựng với các phần phụ thuộc cũ. Bazel yêu cầu bạn chỉ định phiên bản của tất cả phần phụ thuộc theo cách thủ công. Đồng đều ở mức vừa phải, thì mức hao tổn quản lý phiên bản thủ công cũng rất xứng đáng sự ổn định mà nó mang lại.

Quy tắc một phiên bản

Các phiên bản khác nhau của một thư viện thường được biểu thị bằng các cấu phần phần mềm khác nhau, Vì vậy, về mặt lý thuyết, không có lý do nào khiến các phiên bản khác nhau của cùng một bên ngoài bạn không thể khai báo phần phụ thuộc trong hệ thống xây dựng bằng các tên khác nhau. Bằng cách đó, mỗi mục tiêu đều có thể chọn phiên bản phần phụ thuộc mà họ muốn sử dụng. Điều này gây ra rất nhiều vấn đề trong thực tế, vì vậy, Google thực thi một quy tắc Quy tắc một phiên bản cho mọi phần phụ thuộc của bên thứ ba trong cơ sở mã của chúng tôi.

Vấn đề lớn nhất khi cho phép nhiều phiên bản là phần phụ thuộc kim cương vấn đề. Giả sử mục tiêu A phụ thuộc vào mục tiêu B và phụ thuộc vào v1 của bên ngoài thư viện của bạn. Nếu sau đó mục tiêu B được tái cấu trúc để thêm phần phụ thuộc vào v2 của cùng thư viện bên ngoài, thì mục tiêu A sẽ bị hỏng vì giờ đây mục tiêu A phụ thuộc ngầm vào hai các phiên bản khác nhau của cùng một thư viện. Hiệu quả là không bao giờ an toàn để thêm phần phụ thuộc mới từ một đích vào bất kỳ thư viện bên thứ ba nào có nhiều phiên bản, bởi vì bất kỳ người dùng nào trong số những người dùng mục tiêu đó đều có thể đã phụ thuộc vào . Tuân thủ Quy tắc một phiên bản làm cho xung đột này không thể xảy ra—nếu target thêm phần phụ thuộc vào thư viện bên thứ ba, mọi phần phụ thuộc hiện có sẽ sử dụng cùng một phiên bản đó để chúng có thể cùng tồn tại một cách vui vẻ.

Các phần phụ thuộc bên ngoài bắc cầu

Bạn có thể xử lý các phần phụ thuộc bắc cầu của phần phụ thuộc bên ngoài đặc biệt khó khăn. Nhiều kho lưu trữ cấu phần phần mềm, chẳng hạn như Maven Central, cho phép cấu phần phần mềm để chỉ định các phần phụ thuộc trên các phiên bản cụ thể của các cấu phần phần mềm khác trong kho lưu trữ. Các công cụ xây dựng như Maven hoặc Gradle thường tải xuống theo định kỳ từng công cụ phụ thuộc bắc cầu theo mặc định, nghĩa là việc thêm một phần phụ thuộc duy nhất vào dự án của bạn có thể khiến hàng chục cấu phần phần mềm được tải xuống tổng cộng.

Điều này rất tiện lợi: khi thêm phần phụ thuộc vào thư viện mới, rất khó khăn khi theo dõi từng phần phụ thuộc bắc cầu của thư viện đó và thêm chúng theo cách thủ công. Nhưng cũng có một nhược điểm rất lớn: vì khác biệt các thư viện có thể phụ thuộc vào các phiên bản khác nhau của cùng một thư viện bên thứ ba. Điều này vi phạm Quy tắc một phiên bản và dẫn đến kim cương phần phụ thuộc. Nếu mục tiêu của bạn phụ thuộc vào hai thư viện bên ngoài sử dụng các phiên bản khác nhau của cùng một phần phụ thuộc, thì không có cách nào để biết bạn sẽ chọn phiên bản nào nhận được. Điều này cũng có nghĩa là việc cập nhật phần phụ thuộc bên ngoài có thể khiến các lỗi không liên quan trong toàn bộ cơ sở mã nếu phiên bản mới bắt đầu được thêm vào các phiên bản xung đột của một số phần phụ thuộc của nó.

Vì lý do này, Bazel không tự động tải các phần phụ thuộc bắc cầu xuống. Và thật không may, không có giải pháp nào là giải pháp dễ dàng hơn cả – Phương án thay thế của Bazel là cần một tệp toàn cục liệt kê từng phần bên ngoài của kho lưu trữ và một phiên bản rõ ràng dùng cho phần phụ thuộc đó xuyên suốt kho lưu trữ. May mắn là Bazel cung cấp các công cụ có thể tự động tạo một tệp như vậy chứa các phần phụ thuộc bắc cầu của một tập hợp Maven cấu phần phần mềm. Bạn có thể chạy công cụ này một lần để tạo tệp WORKSPACE ban đầu cho một dự án và tệp đó sau đó có thể được cập nhật theo cách thủ công để điều chỉnh các phiên bản của từng phần phụ thuộc.

Một lần nữa, lựa chọn ở đây là một lựa chọn giữa sự thuận tiện và khả năng có thể mở rộng. Nhỏ các dự án có thể không muốn phải lo lắng về việc quản lý các phần phụ thuộc bắc cầu và có thể tránh xa bằng cách sử dụng phương pháp bắc cầu tự động phần phụ thuộc. Chiến lược này ngày càng trở nên ít hấp dẫn hơn khi tổ chức và cơ sở mã tăng lên, các xung đột và kết quả không mong muốn ngày càng nhiều thường xuyên. Ở quy mô lớn hơn, chi phí quản lý các phần phụ thuộc theo cách thủ công là rất lớn ít hơn chi phí xử lý các vấn đề do sự phụ thuộc tự động gây ra Google Cloud.

Lưu kết quả bản dựng vào bộ nhớ đệm bằng cách sử dụng các phần phụ thuộc bên ngoài

Các phần phụ thuộc bên ngoài thường do các bên thứ ba phát hành cung cấp các phiên bản thư viện ổn định mà không cần cung cấp mã nguồn. Hơi nhiều các tổ chức cũng có thể chọn cung cấp một số mã của riêng họ dưới dạng cấu phần phần mềm, cho phép các đoạn mã khác phụ thuộc vào chúng như một bên thứ ba thay vì so với các phần phụ thuộc nội bộ. Về mặt lý thuyết, điều này có thể giúp tăng tốc các bản dựng nếu cấu phần phần mềm xây dựng chậm nhưng tải xuống nhanh.

Tuy nhiên, việc này cũng gây ra nhiều chi phí và độ phức tạp: mọi người cần phải chịu trách nhiệm tạo từng cấu phần phần mềm đó và tải chúng lên kho lưu trữ cấu phần phần mềm và khách hàng cần đảm bảo họ luôn cập nhật thông tin phiên bản mới nhất. Gỡ lỗi cũng trở nên khó khăn hơn nhiều vì các phần của hệ thống sẽ được xây dựng từ các điểm khác nhau trong kho lưu trữ và không còn khung hiển thị nhất quán cho cây nguồn nữa.

Một cách hay hơn để giải quyết vấn đề cấu phần phần mềm mất nhiều thời gian xây dựng là sử dụng một hệ thống xây dựng hỗ trợ chức năng lưu vào bộ nhớ đệm từ xa, như mô tả trước đó. Đúng là hệ thống xây dựng lưu các cấu phần phần mềm kết quả từ mỗi bản dựng vào một vị trí được chia sẻ giữa các kỹ sư, vì vậy, nếu nhà phát triển phụ thuộc vào cấu phần phần mềm do người khác tạo gần đây, nên hệ thống xây dựng sẽ tự động tải xuống thay vì xây dựng nó. Điều này mang lại tất cả lợi ích về hiệu suất của phụ thuộc trực tiếp vào các cấu phần phần mềm trong khi vẫn đảm bảo rằng các bản dựng nhất quán như thể chúng luôn được tạo từ cùng một nguồn. Đây là chiến lược được Google sử dụng nội bộ và Bazel có thể được định cấu hình để sử dụng điều khiển từ xa bộ nhớ đệm.

Tính bảo mật và độ tin cậy của các phần phụ thuộc bên ngoài

Việc phụ thuộc vào cấu phần phần mềm từ các nguồn của bên thứ ba vốn tiềm ẩn rủi ro. Có một rủi ro về khả năng sử dụng nếu nguồn của bên thứ ba (chẳng hạn như kho lưu trữ cấu phần phần mềm) được sử dụng vì toàn bộ bản dựng của bạn có thể bị gián đoạn nếu không thể tải xuống phần phụ thuộc bên ngoài. Ngoài ra, còn có rủi ro về bảo mật: nếu hệ thống của bên thứ ba bị kẻ tấn công xâm phạm, kẻ tấn công có thể thay thế thuộc tính được tham chiếu cấu phần phần mềm có thiết kế riêng, cho phép chúng chèn mã tuỳ ý vào bản dựng của bạn. Bạn có thể giảm thiểu cả hai sự cố bằng cách phản chiếu mọi cấu phần phần mềm mà bạn phụ thuộc vào máy chủ bạn kiểm soát và chặn hệ thống xây dựng của bạn truy cập kho lưu trữ cấu phần phần mềm bên thứ ba như Maven Central. Sự đánh đổi ở đây là những bộ phản chiếu này tốn nhiều công sức và nguồn lực để duy trì, nên việc lựa chọn có việc sử dụng chúng thường phụ thuộc vào quy mô dự án. Vấn đề bảo mật này cũng có thể ngăn chặn hoàn toàn với ít hao tổn bằng cách yêu cầu hàm băm của mỗi cấu phần phần mềm của bên thứ ba được chỉ định trong kho lưu trữ nguồn, khiến bản dựng không thành công nếu cấu phần phần mềm bị can thiệp. Một giải pháp thay thế hoàn toàn khác vấn đề là cung cấp các phần phụ thuộc trong dự án của bạn. Khi một dự án phần phụ thuộc của nhà cung cấp, công cụ này kiểm tra chúng vào chế độ kiểm soát nguồn cùng với mã nguồn của dự án, ở dạng nguồn hoặc dưới dạng tệp nhị phân. Điều này có nghĩa là tất cả phần phụ thuộc bên ngoài của dự án đều được chuyển đổi thành phần phụ thuộc. Google sử dụng phương pháp này trong nội bộ và kiểm tra mọi bên thứ ba thư viện được tham chiếu trên toàn bộ Google vào thư mục third_party ở thư mục gốc của cây nguồn trên Google. Tuy nhiên, phương thức này chỉ hiệu quả tại Google vì là một hệ thống kiểm soát nguồn được thiết kế tuỳ chỉnh để xử lý một kho lưu trữ đơn (monorepo) cực kỳ lớn, vì vậy thì việc cung cấp dịch vụ có thể không phải là lựa chọn dành cho mọi tổ chức.