Khi xem qua các trang trước, bạn sẽ thấy một chủ đề lặp đi lặp lại: việc 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 của mã lại khó khăn hơn nhiều. Có rất nhiều loại phần phụ thuộc: đôi khi có một phần phụ thuộc vào một tác vụ (chẳng hạn như "đẩy tài liệu trước khi tôi đánh dấu bản phát hành là hoàn tất"), 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ã"). Đôi khi, bạn có các phần phụ thuộc nội bộ vào 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). Tuy nhiên, trong mọi trường hợp, ý tưởng "Tôi cần có cái đó trước khi có thể có cái này" là điều lặp đi lặp lại trong thiết kế của hệ thống bản dựng và việc quản lý các phần phụ thuộc có lẽ là công việc cơ bản nhất của một hệ thống bản dựng.
Xử lý mô-đun và phần phụ thuộc
Các dự án sử dụng hệ thống bản 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ác mô-đun, trong đó các mô-đun thể hiện sự phụ thuộc lẫn nhau thông qua các tệp BUILD. Việc tổ chức đúng cách các mô-đun và phần phụ thuộc này có thể ảnh hưởng rất lớn đến cả hiệu suất của hệ thống bản dựng và lượng công việc cần thiết để
duy trì.
Sử dụng mô-đun chi tiết và quy tắc 1:1:1
Câu hỏi đầu tiên xuất hiện khi 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ẻ nên bao gồm. Trong 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ư một
java_library hoặc một go_binary. Ở một cực đoan, toàn bộ dự án có thể được
chứa trong một mô-đun bằng cách đặt một tệp BUILD ở gốc và
đệ quy kết hợp tất cả các tệp nguồn của dự án đó. Ở cực đoan khác, gần như mọi tệp nguồn đều có thể được tạo thành mô-đun riêng, yêu cầu mỗi tệp phải liệt kê trong tệp BUILD mọi tệp khác mà tệp đó phụ thuộc vào.
Hầu hết các dự án đều nằm ở đâu đó giữa hai cực đoan này và lựa chọn liên quan đến sự đánh đổi giữa hiệu suất và khả năng duy 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 phải chạm vào tệp BUILD, ngoại trừ khi thêm một phần phụ thuộc bên ngoài, nhưng điều đó có nghĩa là hệ thống bản dựng phải luôn xây dựng toàn bộ dự án cùng một lúc. Điều này có nghĩa là hệ thống sẽ không thể
song song hoá hoặc phân phối các phần của bản dựng, cũng như không thể lưu vào bộ nhớ đệm các phần
mà hệ thống đã xây dựng. Một mô-đun trên mỗi tệp là ngược lại: hệ thống bản dựng
có tính 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 phải nỗ lực hơn để duy trì danh sách các phần phụ thuộc mỗi khi
họ thay đổi tệp nào tham chiếu đến tệp nào.
Mặc dù độ chi tiết chính xác khác nhau theo ngôn ngữ (và thường là ngay cả trong
ngôn ngữ), nhưng Google có xu hướng ưu tiên các mô-đun nhỏ hơn đáng kể so với những mô-đun mà người ta thường
viết trong hệ thống bản dựng dựa trên tác 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à ngay cả một nhóm có quy mô vừa phải
cũng có thể sở hữu vài 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 mạnh mẽ về việc đóng gói, mỗi thư mục thường
chứa một gói, mục tiêu và BUILD tệp (Pants, một hệ thống bản dựng khác
dựa trên Bazel, gọi đây là quy tắc 1:1:1). Các ngôn ngữ có quy ước đóng gói yếu hơn
thường xuyên xác định nhiều mục tiêu trên 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 ở quy mô lớn vì chúng
dẫn đến các bản dựng phân tán nhanh hơn và ít cần xây dựng lại mục tiêu hơn.
Các lợi thế trở nên hấp dẫn hơn sau khi quá trình kiểm thử bắt đầu, vì
các mục tiêu chi tiết hơn có nghĩa là hệ thống bản dựng có thể thông minh hơn nhiều về
việc chỉ chạy một tập hợp con giới hạn các bài kiểm thử có thể bị ảnh hưởng bởi bất kỳ thay đổi nào. Vì Google tin vào lợi ích hệ thống của việc sử dụng các mục tiêu nhỏ hơn, nên chúng tôi đã có một số tiến bộ trong việc giảm thiểu nhược điểm bằng cách đầu tư vào các công cụ để tự động quản lý các tệp BUILD nhằm tránh gây 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ư buildifier và buildozer, có sẵn với
Bazel trong
buildtools thư mục.
Giảm thiểu khả năng hiển thị mô-đun
Bazel và các hệ thống bản dựng khác cho phép mỗi mục tiêu chỉ định một khả năng hiển thị – một
thuộc tính xác định những mục tiêu khác có thể phụ thuộc vào mục tiêu đó. Một mục tiêu riêng tư
chỉ có thể được tham chiếu trong tệp BUILD của riêng mục tiêu đó. Một mục tiêu có thể cấp khả năng hiển thị rộng hơn
cho các mục tiêu của danh sách tệp BUILD được xác định rõ ràng hoặc, trong
trường hợp khả năng hiển thị công khai, cho mọi mục tiêu trong không gian làm việc.
Giống như hầu hết các ngôn ngữ lập trình, tốt nhất là bạn nên giảm thiểu khả năng hiển thị càng
nhiều càng tốt. Nói chung, các nhóm tại Google sẽ chỉ công khai các 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 nhóm 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 của khách hàng làm khả năng hiển thị của mục tiêu. Các mục tiêu triển khai nội bộ của mỗi
nhóm sẽ chỉ bị hạn chế đối với các thư mục
do nhóm sở hữu và hầu hết các tệp BUILD sẽ chỉ có một mục tiêu không phải là
riêng tư.
Quản lý phần phụ thuộc
Các mô-đun cần có khả năng tham chiếu lẫn nhau. Nhược điểm của việc chia cơ sở mã thành các mô-đun chi tiết là bạn cần quản lý các phần phụ thuộc giữa các mô-đun đó (mặc dù các công cụ có thể giúp tự động hoá việc này). Việc thể hiện các phần phụ thuộc này
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 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ỗ chúng được xây dựng từ nguồn chứ không phải được tải xuống dưới dạng cấu phần phần mềm được 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 các 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 được xây dựng tại cùng một cam kết/bản sửa đổi trong kho lưu trữ. Một vấn đề cần được xử lý cẩn thận liên quan đến các phần phụ thuộc nội bộ là cách xử lý các 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, mục tiêu B phụ thuộc vào mục tiêu thư viện chung C. Mục tiêu A có thể sử dụng các lớp được xác định trong mục tiêu C không?
Hình 1. Phần phụ thuộc bắc cầu
Đối với các công cụ cơ bản, không có vấn đề gì với điều này; cả B và C sẽ được liên kết vào mục tiêu A khi mục tiêu này được xây dựng, vì vậy, mọi ký hiệu được xác định trong C đều được A biết. 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 thấy các vấn đề. Giả sử B được tái cấu trúc sao cho không còn cần phụ thuộc vào C nữa. Nếu phần phụ thuộc của B vào C sau đó bị xoá, thì A và mọi mục tiêu khác sử dụng C thông qua phần phụ thuộc vào B sẽ bị hỏng. Trên thực tế, các phần phụ thuộc của mục tiêu trở thành một phần của hợp đồng công khai và không bao giờ có thể được thay đổi một cách an toàn. Điều này có nghĩa là các phần phụ thuộc tích luỹ theo thời gian và các bản 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 "chế độ phần phụ thuộc bắc cầu nghiêm ngặt" trong Bazel. Ở chế độ này, Bazel phát hiện xem một mục tiêu có cố gắng tham chiếu đến một ký hiệu mà không phụ thuộc trực tiếp vào ký hiệu đó hay không và nếu có, thì sẽ không thành công với lỗi và lệnh shell có thể được dùng để tự động chèn phần phụ thuộc. Việ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 bản dựng của chúng tôi để liệt kê rõ ràng các phần phụ thuộc của chúng là một nỗ lực kéo dài nhiều năm, nhưng rất đáng giá. Các bản dựng của chúng tôi hiện 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 xoá các phần phụ thuộc mà họ không cần mà không phải lo lắng về việc làm hỏng các mục tiêu 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 liên quan đến sự đánh đổi. Điều này khiến
các tệp bản dựng trở nên chi tiết hơn, vì các thư viện thường dùng hiện cần được liệt kê
rõ ràng ở nhiều nơi thay vì được kéo vào một cách ngẫu nhiên và các kỹ sư
cần phải nỗ lực hơn để thêm các phần phụ thuộc vào tệp BUILD files. Kể từ đó, chúng tôi đã phát triển các công cụ giúp giảm bớt công việc này bằng cách tự động phát hiện nhiều phần phụ thuộc bị thiếu và thêm chúng vào tệp BUILD mà không cần sự can thiệp của nhà phát triển. 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 này rất đáng
giá khi cơ sở mã mở rộng: việc thêm rõ ràng một phần phụ thuộc 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 ẩn có thể gây
ra các vấn đề liên tục miễn là mục tiêu bản dựng tồn tại. Theo mặc định, Bazel
thực thi các phần phụ thuộc bắc cầu nghiêm ngặt
trên mã Java.
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ần phụ thuộc đó phải là bên ngoài. Các phần phụ thuộc bên ngoài là những phần phụ thuộc trên các cấu phần phần mềm được xây dựng và lưu trữ bên ngoài hệ thống bản dựng. 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 xây dựng từ nguồn. Một trong những điểm khác biệt lớn nhất giữa các phần phụ thuộc bên ngoài và nội bộ 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 bản dựng có thể cho phép quản lý các phiên bản của 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
sẽ liệt kê rõ ràng phiên bản mà tệp đó 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 như
1.1.4. Khi được quản lý tự động, tệp nguồn sẽ chỉ định một phạm vi
phiên bản chấp nhận được và hệ thống bản dựng luôn tải xuống phiên bản mới nhất. Ví
dụ: Gradle cho phép khai báo phiên bản phần phụ thuộc là "1.+" để chỉ định
rằng mọi phiên bản phụ hoặc phiên bản vá của phần phụ thuộc đều có thể chấp nhận được miễn là phiên bản chính 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 cho thảm hoạ đối với các dự án có quy mô không nhỏ hoặc đang được thực hiện bởi nhiều kỹ sư. Vấn đề với các phần phụ thuộc được quản lý tự động là bạn không kiểm soát được thời điểm phiên bản được cập nhật. Không có cách nào để đảm bảo rằng các bên bên ngoài sẽ không thực hiện các bản cập nhật gây lỗi (ngay cả khi họ tuyên bố sử dụng tính năng kiểm soát phiên bản ngữ nghĩa), vì vậy, một bản dựng hoạt động vào một ngày có thể bị hỏng vào ngày hôm sau mà không có cách dễ dàng để phát hiện những gì đã thay đổi hoặc để khôi phục về trạng thái hoạt động. Ngay cả khi bản dựng không bị hỏng, vẫn có thể có những thay đổi tinh vi về hành vi hoặc hiệu suất mà 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 yêu cầu thay đổi trong hệ thống kiểm soát nguồn, nên chúng có thể dễ dàng được phát hiện và khôi phục, đồng thời có thể kiểm tra phiên bản cũ hơn của kho lưu trữ để xây dựng bằng các phần phụ thuộc cũ hơn. Bazel yêu cầu phải chỉ định phiên bản của tất cả các phần phụ thuộc theo cách thủ công. Ngay cả ở quy mô vừa phải, chi phí quản lý phiên bản theo cách thủ công cũng rất đáng giá cho 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ề lý thuyết, không có lý do gì mà cả hai phiên bản khác nhau của cùng một phần phụ thuộc bên ngoài không thể được khai báo trong hệ thống bản dựng dưới các tên khác nhau. Bằng cách đó, mỗi mục tiêu có thể chọn phiên bản phần phụ thuộc mà mục tiêu đó muốn sử dụng. Điều này gây ra nhiều vấn đề trong thực tế, vì vậy, Google thực thi một Quy tắc một phiên bản nghiêm ngặt cho tất cả các 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à vấn đề về phần phụ thuộc hình kim cương. Giả sử mục tiêu A phụ thuộc vào mục tiêu B và vào phiên bản 1 của một thư viện bên ngoài. Nếu mục tiêu B sau đó được tái cấu trúc để thêm phần phụ thuộc vào phiên bản 2 của cùng một thư viện bên ngoài, thì mục tiêu A sẽ bị hỏng vì mục tiêu này hiện phụ thuộc ngầm ẩn vào hai phiên bản khác nhau của cùng một thư viện. Trên thực tế, không bao giờ an toàn khi thêm một phần phụ thuộc mới từ một mục tiêu vào bất kỳ thư viện nào của bên thứ ba có nhiều phiên bản, vì bất kỳ người dùng nào của mục tiêu đó cũng có thể đã phụ thuộc vào một phiên bản khác. Việc tuân theo Quy tắc một phiên bản khiến xung đột này không thể xảy ra – nếu một mục tiêu thêm phần phụ thuộc vào một thư viện của bên thứ ba, thì mọi phần phụ thuộc hiện có sẽ đã ở cùng phiên bản đó, vì vậy, chúng có thể cùng tồn tại một cách an toàn.
Phần phụ thuộc bên ngoài bắc cầu
Việc xử lý các phần phụ thuộc bắc cầu của một phần phụ thuộc bên ngoài có thể đặc biệt khó khăn. Nhiều kho lưu trữ cấu phần phần mềm như Maven Central cho phép các cấu phần phần mềm chỉ định các phần phụ thuộc vào 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ụ bản dựng như Maven hoặc Gradle thường tải xuống đệ quy từng phần 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 trong 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 thuận tiện: khi thêm phần phụ thuộc vào một thư viện mới, bạn sẽ gặp nhiều khó khăn khi phải theo dõi từng phần phụ thuộc bắc cầu của thư viện đó và thêm tất cả theo cách thủ công. Nhưng cũng có một nhược điểm rất lớn: vì các thư viện khác nhau 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, nên chiến lược này nhất thiết vi phạm Quy tắc một phiên bản và dẫn đến vấn đề về phần phụ thuộc hình kim cương. 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ì bạn không thể biết mình sẽ nhận được phiên bản nào. Điều này cũng có nghĩa là việc cập nhật một phần phụ thuộc bên ngoài có thể gây ra các lỗi dường như không liên quan trong toàn bộ cơ sở mã nếu phiên bản mới bắt đầu kéo vào các phiên bản xung đột của một số phần phụ thuộc.
Vì lý do này, Bazel không tự động tải xuống các phần phụ thuộc bắc cầu.
Và thật không may, không có giải pháp nào hoàn hảo – giải pháp thay thế của Bazel là yêu cầu một
tệp toàn cục liệt kê từng phần phụ thuộc bên ngoài của kho lưu trữ và một phiên bản rõ ràng được dùng cho phần phụ thuộc đó trong toàn bộ kho lưu trữ. May mắn thay, 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 các cấu phần phần mềm Maven
Công cụ này có thể được chạy một lần để tạo tệp WORKSPACE ban đầu
cho một dự án và sau đó, bạn có thể cập nhật tệp đó theo cách thủ công để điều chỉnh 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à giữa sự thuận tiện và khả năng mở rộng. Các dự án nhỏ có thể không muốn lo lắng về việc tự quản lý các phần phụ thuộc bắc cầu và có thể sử dụng các phần phụ thuộc bắc cầu tự động. Chiến lược này ngày càng kém hấp dẫn khi tổ chức và cơ sở mã phát triển, đồng thời các xung đột và kết quả không mong muốn ngày càng thường xuyên xảy ra. Ở quy mô lớn hơn, chi phí quản lý các phần phụ thuộc theo cách thủ công thấp hơn nhiều so với chi phí xử lý các vấn đề do tính năng quản lý phần phụ thuộc tự động gây ra.
Lưu kết quả bản dựng vào bộ nhớ đệm bằ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 được cung cấp bởi các bên thứ ba phát hành các phiên bản ổn định của thư viện, có thể là không cung cấp mã nguồn. Một số 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 dưới dạng phần phụ thuộc bên thứ ba thay vì phần phụ thuộc nội bộ. Về lý thuyết, điều này có thể tăng tốc các bản dựng nếu các cấu phần phần mềm xây dựng chậm nhưng tải xuống nhanh.
Tuy nhiên, điều này cũng làm tăng thêm nhiều chi phí và độ phức tạp: ai đó cần chịu trách nhiệm xây dựng 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, đồng thời các ứng dụng cần đảm bảo rằng chúng luôn cập nhật phiên bản mới nhất. Việc gỡ lỗi cũng trở nên khó khăn hơn nhiều vì các phần khác nhau 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 có chế độ xem nhất quán về cây nguồn.
Một cách tốt hơn để giải quyết vấn đề về việc các cấu phần phần mềm mất nhiều thời gian để xây dựng là sử dụng hệ thống bản dựng hỗ trợ tính năng lưu vào bộ nhớ đệm từ xa, như mô tả trước đó. Hệ thống bản dựng như vậy sẽ 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 một nhà phát triển phụ thuộc vào một cấu phần phần mềm do người khác xây dựng gần đây, thì hệ thống bản dựng sẽ tự động tải xuống cấu phần phần mềm đó thay vì xây dựng. Điều này mang lại tất cả các lợi ích về hiệu suất của việc 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 xây dựng từ cùng một nguồn. Đây là chiến lược được Google sử dụng nội bộ và bạn có thể định cấu hình Bazel để sử dụng bộ nhớ đệm từ xa.
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ác cấu phần phần mềm từ các nguồn bên thứ ba vốn dĩ là rủi ro. Có rủi ro về tính khả dụng nếu nguồn bên thứ ba (chẳng hạn như kho lưu trữ cấu phần phần mềm) ngừng hoạt động, vì toàn bộ bản dựng của bạn có thể bị dừng lại nếu không tải xuống được phần phụ thuộc bên ngoài. Cũng 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 nhập, kẻ tấn công có thể thay thế cấu phần phần mềm được tham chiếu
bằng một cấu phần phần mềm do chính họ thiết kế, cho phép họ chèn mã tuỳ ý
vào bản dựng của bạn. Cả hai vấn đề đều có thể được giảm thiểu bằng cách sao chép mọi cấu phần phần mềm mà bạn
phụ thuộc vào các máy chủ mà bạn kiểm soát và chặn hệ thống bản dựng của bạn truy cập vào
các kho lưu trữ cấu phần phần mềm của bên thứ ba như Maven Central. Sự đánh đổi là
các bản sao này tốn nhiều công sức và tài nguyên để duy trì, vì vậy, việc lựa chọn có
sử dụng chúng hay không thường phụ thuộc vào quy mô của dự án. Vấn đề bảo mật cũng có thể
được ngăn chặn hoàn toàn với ít chi phí bằng cách yêu cầu chỉ định hàm băm của từng
cấu phần phần mềm của bên thứ ba 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ị giả mạo. Một giải pháp thay thế khác hoàn toàn
bỏ qua vấn đề này là cung cấp các phần phụ thuộc của dự án. Khi một dự án
cung cấp các phần phụ thuộc, dự án đó sẽ kiểm tra các phần phụ thuộc vào hệ thống kiểm soát nguồn cùng với
mã nguồn của dự án, dưới 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ả các phần phụ thuộc bên ngoài của dự án đều được chuyển đổi thành các phần phụ thuộc nội bộ
Google sử dụng phương pháp này nội bộ, kiểm tra mọi thư viện của bên thứ ba
được tham chiếu trong toàn bộ Google vào thư mục third_party ở gốc
của cây nguồn của Google. Tuy nhiên, phương pháp này chỉ hoạt động tại Google vì hệ thống kiểm soát nguồn của Google
được xây dựng tuỳ chỉnh để xử lý một kho lưu trữ đơn cực lớn, vì vậy,
việc cung cấp có thể không phải là lựa chọn cho tất cả các tổ chức.
