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

Báo cáo sự cố Xem nguồn

Khi xem qua các trang trước, 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 lại khó khăn hơn nhiều. Nhưng trong mọi trường hợp, ý tưởng “Tôi cần điều đó trước khi có thể có điều này” là điều lặp đi lặp lại trong quá trình 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ông việc cơ bản nhất 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 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ác mô-đun, với các mô-đun thể hiện các phần phụ thuộc trên nhau thông qua các tệp BUILD. Việc sắp xếp đúng cách các mô-đun và phần phụ thuộc này có thể có ảnh hưởng rất lớn đến hiệu suất của hệ thống xây dựng và khối lượng công việc cần duy trì.

Sử dụng các mô-đun hạt mịn và quy tắc 1:1:1

Câu hỏi đầu tiên được đưa ra 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 của một mô-đun riêng lẻ. 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ể tạo như java_library hoặc go_binary. Ở một mức độ cực đoan, toàn bộ dự án có thể nằm trong một mô-đun duy nhất bằng cách đặt một tệp BUILD vào thư mục gốc và gắn kết đệ quy tất cả các tệp nguồn của dự án đó. Ngoài ra, gần như mọi tệp nguồn đều có thể được tạo thành mô-đun riêng, giúp đòi hỏi từng tệp phải có trong danh sách trong một tệp BUILD mà mọi tệp khác đều phụ thuộc vào.

Hầu hết các dự án đều nằm ở những điểm 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 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 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 xây dựng phải luôn tạo toàn bộ dự án cùng một lúc. Điều này có nghĩa là nó sẽ không thể tải song song hoặc phân phối các phần của bản dựng cũng như không thể lưu các bộ nhớ đệm mà bạn đã tạo vào bộ nhớ đệm. Một mô-đun trên mỗi tệp thì ngược lại: hệ thống xây 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 kỹ sư cần tăng cường nỗ lực duy trì danh sách các phần phụ thuộc bất cứ khi nào họ thay đổi tệp nào tham chiếu đến tệp đó.

Mặc dù độ chi tiết chính xác thay đổi theo ngôn ngữ (và thường là ngay 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 mô-đun thường có thể viết trong hệ thống xây dựng dựa trên tác vụ. Tệp nhị phân sản xuất thông thường tại Google thường phụ thuộc vào hàng chục nghìn mục tiêu và thậm chí 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 nó. Đối với những ngôn ngữ như Java có tích hợp sẵn ý tưởng đóng gói, mỗi thư mục thường chứa một gói, mục tiêu và tệp BUILD duy nhất (Pants, một hệ thống xây dựng khác dựa trên Bazel, gọi quy tắc 1:1:1). Các ngôn ngữ có quy ước đóng gói yếu hơn thường xác định nhiều mục tiêu cho mỗi tệp BUILD.

Lợi ích của mục tiêu bản dựng nhỏ hơn thực sự bắt đầu xuất hiện trên quy mô lớn vì các mục tiêu này giúp tạo 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ế càng trở nên hấp dẫn hơn sau khi thử nghiệm đi vào tổng thể, 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ể thông minh hơn nhiều về việc chỉ chạy một nhóm nhỏ các thử nghiệm có thể bị ảnh hưởng bởi bất kỳ thay đổi nào. Vì tin tưởng vào lợi ích có hệ thống của việc sử dụng các mục tiêu nhỏ, nên chúng tôi đã thực hiện một số bước tiến trong việc giảm thiểu nhược điểm bằng cách đầu tư vào việc tự động quản lý các tệp BUILD để 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ư buildifierbuildozer, có sẵn với Bazel trong thư mục buildtools.

Giảm thiểu mức độ 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 chế độ hiển thị – một thuộc tính xác định các mục tiêu khác có thể phụ thuộc vào nó. Mục tiêu riêng chỉ có thể được tham chiếu trong tệp BUILD riêng. 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.

Như với hầu hết các ngôn ngữ lập trình khác, tốt nhất bạn nên giảm thiểu khả năng hiển thị nhiều nhất có thể. Nhìn chung, các nhóm tại Google sẽ chỉ công khai mục tiêu nếu các mục tiêu đó đại diện cho các thư viện được sử dụng rộng rãi cho bất kỳ nhóm nào tại Google. Các nhóm yêu cầu người khác phải 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ủa mục tiêu khách hàng làm mức độ 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ẽ bị hạn chế chỉ ở những 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à mục tiêu riêng tư.

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

Các mô-đun cần có thể tham chiếu đế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 trong số các mô-đun đó (mặc dù các công cụ có thể giúp tự động hóa 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 đều có thể là nội bộ; nghĩa 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 bên trong khác với các phần phụ thuộc bên ngoài ở chỗ 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 đượ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 phần phụ thuộc nội bộ. Mục tiêu và tất cả phần phụ thuộc nội bộ của phần tử đó luôn được tạo ở cùng một cam kết/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 này phụ thuộc vào mục tiêu C của thư viện chung. 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?

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

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

Về công cụ cơ bản thì không có vấn đề gì; cả B và C đều sẽ được liên kết với mục tiêu A khi được tạo, 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 nhiều vấn đề. Giả sử B đã được tái cấu trúc sao cho không cần phụ thuộc vào C nữa. Nếu phần phụ thuộc của B trên C bị xoá sau đó, thì A và mọi mục tiêu khác sử dụng C thông qua phần phụ thuộc trên B sẽ bị hỏng. Một cách hiệu quả, các phần phụ thuộc của mục tiêu đã trở thành một phần trong hợp đồng công khai và không bao giờ có thể thay đổi một cách an toàn. Điều này có nghĩa là các phần phụ thuộc được tích luỹ theo thời gian và các bản dựng tại Google bắt đầu chạy chậm lại.

Cuối cùng, Google đã giải quyết vấn đề này bằng cách ra mắt "chế độ phụ thuộc bắc cầu nghiêm ngặt" trong Bazel. Ở chế độ này, Bazel sẽ phát hiện xem một mục tiêu có cố gắng tham chiếu đến một biểu tượng mà không phụ thuộc trực tiếp vào biểu tượng hay không. Nếu có thì sẽ xảy ra lỗi và một lệnh shell có thể 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 xây dựng để liệt kê rõ các phần phụ thuộc là một nỗ lực trong 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 rất 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 tạo điều kiệ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 mục tiêu bị hỏng sẽ phụ thuộc vào các mục tiêu đó.

Như thường lệ, việc thực thi các phần phụ thuộc bắc cầu nghiêm ngặt bao gồm sự đánh đổi. Điều này làm cho các tệp bản dựng chi tiết hơn, vì các thư viện được sử dụng thường xuyên hiện cần được liệt kê rõ ràng ở nhiều vị trí hơn là được lồng vào nhau và các kỹ sư cần tốn nhiều công sức hơn để thêm các phần phụ thuộc vào tệp BUILD. Kể từ đó, chúng tôi đã phát triển các công cụ giúp giảm thiểu công việc cực nhọ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 các phần đó vào tệp BUILD mà không cần sự can thiệp của nhà phát triển. Tuy nhiên, ngay cả khi không có các công cụ như vậy, chúng tôi vẫn nhận thấy sự đánh đổi đáng giá khi quy mô cơ sở mã: việc thêm phần phụ thuộc 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 sự cố đang diễn ra, 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 phần phụ thuộc không phải 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à các 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 xây 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 tạo 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ó cá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 buildbuild sẽ liệt kê rõ ràng 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ư 1.1.4). Khi được quản lý tự động, tệp nguồn chỉ định một loạt các phiên bản được chấp nhận 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 một phiên bản phần phụ thuộc là “1.+” để chỉ định rằng mọi phiên bản phụ hoặc 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 các phần phụ thuộc này thường là công thức cho thảm họa về các dự án có quy mô không quan trọng hoặc do nhiều kỹ sư thực hiện. Vấn đề với các phần phụ thuộc được quản lý tự động là bạn không có quyền kiểm soát thời điểm cập nhật phiên bản. Không có cách nào để đảm bảo rằng các bên ngoài sẽ không thực hiện cập nhật gây lỗi (ngay cả khi họ tuyên bố sử dụng phiên bản ngữ nghĩa), vì vậy, bản dựng hoạt động một ngày có thể bị hỏng vào ngày tiếp theo mà không có cách nào 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, có thể có những thay đổi nhỏ về hành vi hoặc hiệu suất mà bạn không thể theo dõi.

Ngược lại, vì các phần phụ thuộc được quản lý thủ công đòi hỏi thay đổi quyền kiểm soát nguồn, nên bạn có thể dễ dàng phát hiện và khôi phục các phần phụ thuộc đó. Bạn cũng có thể xem phiên bản kho lưu trữ cũ hơn để tạo các phần phụ thuộc cũ. Bazel yêu cầu bạn phải chỉ định thủ công các phiên bản của tất cả phần phụ thuộc theo cách thủ công. Ngay cả ở quy mô vừa phải, mức hao tổn quản lý phiên bản thủ công cũng rất xứng đáng cho độ ổn định mà công cụ này mang lại.

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

Các phiên bản khác nhau của 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 trên lý thuyết, không có lý do gì để không thể khai báo các phiên bản khác nhau của cùng một phần phụ thuộc bên ngoài 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 của phần phụ thuộc mà nó 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 Quy tắc một phiên bản nghiêm ngặt cho tất cả các phần phụ thuộc 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 kim cương. Giả sử mục tiêu A phụ thuộc vào mục tiêu B và trên v1 của 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ì hiện phụ thuộc ngầm vào hai 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 khi thêm phần phụ thuộc mới từ một mục tiêu vào bất kỳ thư viện bên thứ ba nào có nhiều phiên bản, vì bất kỳ người dùng nào của mục tiêu đó đều có thể đã phụ thuộc vào một phiên bản khác. Việc tuân thủ Quy tắc một phiên bản sẽ gây ra xung đột này – nếu một mục tiêu thêm phần phụ thuộc vào thư viện bên thứ ba, thì mọi phần phụ thuộc hiện có sẽ có trên cùng một phiên bản để có thể cùng tồn tại một cách vui vẻ.

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 (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ấ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 định kỳ từng phần phụ thuộc bắc cầu, nghĩa là việc thêm một phần phụ thuộc duy nhất vào dự án có thể khiến tổng số cấu phần phần mềm bị tải xuống.

Điều này rất thuận tiện: khi thêm một phần phụ thuộc vào một thư viện mới, bạn sẽ rất đau đầu 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ả các phần đó theo cách thủ công. Tuy nhiên, có một nhược điểm rất lớn là do 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 phải vi phạm Quy tắc một phiên bản và dẫn đến vấn đề về 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ì bạn không 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 phần phụ thuộc bên ngoài có thể gây ra các lỗi có vẻ không liên quan trên toàn bộ cơ sở mã nếu phiên bản mới bắt đầu lấy 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 các phần phụ thuộc bắc cầu xuống. Và thật không may là không có giải pháp nào — giải pháp thay thế của Babel 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 sử dụng cho phần phụ thuộc đó trong toàn bộ 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 nhóm cấu phần phần mềm Maven. Bạn có thể chạy công cụ này một lần để tạo tệp WORKSPACE ban đầu cho dự án, sau đó cập nhật tệp đó 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.

Xin nhắc lại, lựa chọn ở đây là lựa chọn giữa sự tiện lợi và khả năng mở rộng. Các dự án nhỏ có thể không muốn lo lắng về việc quản lý các phần phụ thuộc bắc cầu và có thể không cần 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 ít hấp dẫn hơn khi tổ chức và cơ sở mã ngày càng phát triển, xung đột và kết quả không mong muốn ngày càng trở nên thường xuyên hơ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 sẽ thấp hơn nhiều so với chi phí xử lý các vấn đề do quản lý phần phụ thuộc tự động.

Lưu các kết quả của 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 được bên thứ ba cung cấp nhất có thể phát hành các phiên bản thư viện ổn định, có thể là không cần 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 mình 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 bên thứ ba thay vì các phần phụ thuộc nội bộ. Điều này về mặt lý thuyết có thể giúp tăng tốc độ các bản dựng nếu các cấu phần phần mềm quá trình xây dựng bị chậm nhưng nhanh chóng tải xuống.

Tuy nhiên, điều này cũng gây ra nhiều hao tổn và độ phức tạp: một người nào đó cần phải có trách nhiệm xây dựng từng cấu phần phần mềm đó và tải lên kho lưu trữ cấu phần phần mềm, đồng thời khách hàng cần đảm bảo rằng họ 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ừ nhiều điểm trong kho lưu trữ và không còn có chế độ xem nhất quán về cây nguồn.

Cách tốt 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ợ lưu vào bộ nhớ đệm từ xa, như mô tả ở trên. Hệ thống bản dựng như vậy lưu các cấu phần phần mềm thu được 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 một cấu phần phần mềm mà gần đây đã được người khác tạo, hệ thống bản dựng sẽ tự động tải xuống thay vì tạo. Điều này mang lại tất cả lợi ích về hiệu suất khi trực tiếp dựa 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ể các bản dựng này luôn được tạo từ cùng một nguồn. Đây là chiến lược mà Google sử dụng nội bộ và Bazel có thể được định cấu hình để 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

Tùy thuộc vào cấu phần phần mềm từ các nguồn của bên thứ ba, tiềm năng là rủi ro. Sẽ có rủi ro về khả năng truy cập 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) bị giảm xuống, vì toàn bộ bản dựng có thể bị tạm dừng nếu không thể tải phần phụ thuộc bên ngoài xuống. Cũng có một rủi ro bảo mật: nếu hệ thống 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 thiết kế của riêng mình, 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 vấn đề này bằng cách phản ánh 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 xây dựng truy cập vào kho lưu trữ cấu phần phần mềm của bên thứ ba như Maven Central. Sự đánh đổi ở đây là những bản sao này mất nhiều công sức và tài nguyên để duy trì, vì vậy, việc lựa chọn sử dụng chúng thường hay không phụ thuộc vào quy mô của dự án. Bạn cũng có thể ngăn chặn hoàn toàn vấn đề bảo mật với chi phí thấp bằng cách yêu cầu băm của mỗi cấu phần phần mềm bên thứ ba trong kho lưu trữ nguồn, khiến bản dựng này 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ế khác giúp ngăn chặn hoàn toàn vấn đề này là cung cấp các phần phụ thuộc cho dự án. Khi cung cấp các phần phụ thuộc, dự án sẽ kiểm tra các phần phụ thuộc đó cùng với mã nguồn của dự án, dưới dạng mã nguồn hoặc tệp nhị phân. Điều này hiệu quả 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 các phần phụ thuộc nội bộ. Google sử dụng phương pháp này trong nội bộ, kiểm tra mọi thư viện của bên thứ ba được tham chiếu trên toàn Google trong một thư mục third_party ở gốc cây Google. Tuy nhiên, phương thức này chỉ hoạt động tại Google vì hệ thống kiểm soát nguồn của Google được thiết kế tuỳ chỉnh để xử lý một máy chạy monorepo cực lớn, vì vậy, việc cung cấp có thể không phải là một lựa chọn dành cho tất cả các tổ chức.