Monorepos đã nổi lên như một chiến lược phổ biến để quản lý các codebase rộng lớn do cách triển khai đơn giản của chúng. Tuy nhiên, cách tiếp cận này mang lại những thách thức độc đáo, đặc biệt là khi sử dụng một hệ thống build hiểu được các bối cảnh dự án khác nhau.
Bài viết này tìm hiểu lý do tại sao các hệ thống build truyền thống thất bại trong bối cảnh monorepos và khám phá các giải pháp hiện đại để tối ưu hóa các quy trình CI/CD.
Monorepo là gì?
Monorepo là một kho mã nguồn duy nhất được kiểm soát phiên bản chứa nhiều dự án hoặc module. Không giống như các thiết lập multi-repo, nơi mỗi dự án có kho riêng, monorepos hợp nhất các dự án liên quan vào một kho. Việc hợp nhất này mang lại một số lợi ích:
- Cải thiện sự hợp tác: Các đội làm việc trên các dự án khác nhau có thể hợp tác hiệu quả hơn.
- Quản lý phụ thuộc đơn giản hóa: Các phụ thuộc giữa các dự án dễ quản lý hơn trong một kho duy nhất.
- Dễ dàng chia sẻ mã: Các nhà phát triển có thể dễ dàng chia sẻ và tái sử dụng mã giữa các dự án khác nhau.
Các công ty như Google, Facebook, và Microsoft đã triển khai thành công monorepos, nâng cao quy trình phát triển và hợp tác giữa các đội. Tuy nhiên, đối với nhiều doanh nghiệp, việc quản lý monorepos đòi hỏi phải sử dụng các công cụ bên thứ ba và các hệ thống build toàn diện để xử lý sự phức tạp của chúng.
Những thách thức trong hệ thống build của Monorepo
Việc triển khai các pipeline CI/CD trong thiết lập monorepo mang lại những thách thức độc đáo do tính liên kết của codebase. Những thách thức này bao gồm:
- Các pipeline build và kiểm thử phức tạp: Các pipeline tinh vi cần thiết để xử lý các phụ thuộc và thay đổi trên nhiều dự án.
- Build tăng dần: Việc giảm thời gian build thông qua các build tăng dần là rất quan trọng nhưng khó đạt được với các hệ thống build truyền thống.
- Thực thi song song: Thực thi song song hiệu quả của các build và kiểm thử là điều cần thiết để tăng tốc quy trình CI/CD.
- Quản lý phụ thuộc và phiên bản: Đảm bảo sự nhất quán và tương thích giữa các dự án trong một monorepo đòi hỏi quản lý cẩn thận.
Ví dụ: Monorepo cho các lambda functions
Xem xét một monorepo chứa nhiều Lambda functions:
project-root/
├── api-gateway/
│ └── ...
├── lambdas/
│ ├── count-lambda/
│ │ ├── count-lambda.docs.mdx
│ │ ├── count-lambda.ts
│ │ └── index.ts
│ ├── date-lambda/
│ └── ...
├── utils/
│ ├── date-util/
│ └── ...
Mỗi Lambda function có vòng đời riêng từ phát triển đến runtime. Thiết lập CI/CD cho các chức năng này mất nhiều thời gian. Ví dụ, nếu date-util được sửa đổi, các chức năng date-lambda và hello-lambda cũng phải được triển khai. Hệ thống CI phải được cấu hình để nhận biết các phụ thuộc này và hành động theo đó.
Việc sửa đổi nhiều thành phần cùng một lúc mang lại sự phức tạp bổ sung. Hệ thống CI phải xác định thứ tự phụ thuộc và build theo thứ tự, trong khi các thành phần không phụ thuộc có thể chạy song song để giảm thời gian build. Các quy trình phức tạp cho các phụ thuộc và phiên bản có thể làm cho hệ thống build trở nên mong manh và trở thành điểm tắc nghẽn.
Các giải pháp hiện đại cho CI/CD của Monorepo
Các công cụ hiện đại giải quyết các hạn chế của hệ thống build truyền thống và những thách thức của monorepos. Các công cụ này có thể được phân loại thành các công cụ kiến trúc và công cụ tiện ích.
Các công cụ kiến trúc cho Monorepos
Các công cụ kiến trúc giúp xác định cấu trúc của monorepo, giải quyết các thách thức cơ bản. Một công cụ như vậy là Bit, sử dụng các component để tạo monorepos với các ranh giới riêng biệt. Mỗi component có thể là một module frontend hoặc backend với chức năng cụ thể, cấu hình và tài liệu.
Bit cho phép hệ thống CI build và triển khai chỉ các component đã được sửa đổi và các phụ thuộc của chúng, tối ưu hóa quy trình build. Các phiên bản khác nhau của các component cho phép sự linh hoạt cho các dự án nâng cấp theo tốc độ riêng của chúng.
Các công cụ tiện ích cho Monorepos
Các công cụ tiện ích cung cấp các chức năng để giải quyết các thách thức cụ thể trong monorepos. Các công cụ đáng chú ý bao gồm:
- Nx: Mở rộng các hệ thống build truyền thống với các tính năng như build tăng dần, quản lý phụ thuộc hiệu quả và thực thi song song.
- Bazel: Được Google thiết kế cho các codebase quy mô lớn, hỗ trợ build tăng dần và thực thi song song.
- Lerna: Quản lý các dự án JavaScript với nhiều gói, cung cấp tính năng nâng cấp phụ thuộc và build tăng dần.
- Turborepo: Hệ thống build hiệu suất cao tập trung vào tốc độ và hiệu quả, với các tính năng như caching và thực thi song song.
Các công cụ này đảm bảo chỉ có mã sửa đổi được build, sử dụng các build artifacts từ cache cho mã không thay đổi.
Kết luận
Monorepos mang lại nhiều lợi thế về chia sẻ mã, hợp tác và quản lý phụ thuộc. Tuy nhiên, các hệ thống build truyền thống gặp khó khăn với sự phức tạp của chúng. Các công cụ CI/CD hiện đại như Bit, Nx, Bazel, Lerna và Turborepo tối ưu hóa các quy trình và cải thiện hiệu suất. Việc áp dụng monorepos có thể ghép và sử dụng các công cụ hiện đại có thể dẫn đến thời gian build nhanh hơn, sử dụng tài nguyên hiệu quả và tăng năng suất của nhà phát triển.
Cảm ơn bạn đã đọc. Chúc sức khỏe!