Bạn đang làm một dự án. Frontend ở một repo, backend ở repo khác, và thư viện dùng chung ở repo thứ ba. Bạn phải liên tục chuyển repo và hy vọng phiên bản không bị lệch nhau.
Giờ hãy tưởng tượng mọi thứ ở cùng một nơi. Một repo cho tất cả dự án của bạn, mã dùng chung được cập nhật ngay lập tức, và thay đổi lan tỏa chỉ trong một commit.
Monorepo biến điều đó thành hiện thực. Đó là lý do nhiều công ty công nghệ lớn cũng sử dụng, như:
Trong hướng dẫn này, bạn sẽ biết monorepo là gì, cách so sánh với polyrepo, cùng các lợi ích, thách thức và thực tiễn tốt nhất cần nắm.
Monorepo là gì?
Monorepo (monolithic repository) nghĩa là một kho lưu trữ duy nhất. Trong monorepo, tất cả các dự án khác nhau của bạn cùng nằm ở một nơi, thay vì tách ra nhiều kho riêng.
Dù các dự án nằm cạnh nhau, chúng vẫn độc lập. Tức là bạn có thể build, test và triển khai một cách độc lập, đồng thời được lợi khi giữ mọi thứ tập trung ở một chỗ.
Nhưng lưu ý: ứng dụng nguyên khối (monolithic application) và monorepo không phải là một:
- Ứng dụng nguyên khối (monolith) là một ứng dụng phần mềm được xây dựng và chạy như một khối thống nhất, thường làm việc trên cùng một bộ dữ liệu. Nó có thể nằm trong monorepo (như một trong số nhiều dự án).
- Monorepo lưu trữ và quản lý mã sao cho nhiều dự án cùng sống trong một repo, nhưng vẫn độc lập với nhau.
Monorepo không phải là ứng dụng nguyên khối. Thực tế, monorepo thường phối hợp rất tốt với microservices, vì mỗi service có thể sống trong cùng repo mà vẫn độc lập.
Khái niệm monorepo không mới.
Đầu những năm 2000, các công ty công nghệ lớn bắt đầu áp dụng cách tiếp cận “shared codebase”. Ý tưởng rất đơn giản: thay vì rải mã ra nhiều repo, đưa tất cả vào một chỗ để việc cộng tác dễ dàng hơn.
Kể từ đó, các công ty lớn như Google, Meta, Microsoft, Airbnb, Twitter (X), Uber và Pinterest đã áp dụng monorepo để quản lý codebase lớn và thay đổi nhanh của họ.
Monorepo vs. Polyrepo
Đối lập với monorepo là polyrepo (multirepo). Hãy xem sự khác biệt.
Polyrepo
Polyrepo, còn gọi là multirepo, là khi mỗi dự án có kho lưu trữ riêng biệt.
Ví dụ, mã frontend có thể ở một kho, backend ở kho khác, và thư viện dùng chung ở kho riêng. Mỗi kho được quản lý độc lập với dependency và quy trình riêng.

Monorepo so với polyrepo. Hình minh họa của Tác giả.
Nhìn nhanh sự khác biệt chính
Hãy xem những khác biệt chính giữa monorepo và polyrepo:
|
Monorepo
|
Polyrepo
|
|
Một kho lưu trữ cho tất cả dự án và mã dùng chung.
|
Mỗi dự án có kho riêng biệt.
|
|
Dễ chia sẻ và tái sử dụng mã vì mọi thứ ở cùng một repo.
|
Khó chia sẻ mã hơn vì dự án có thể trùng lặp công việc hoặc lệch nhịp.
|
|
Quản lý tập trung nên phiên bản nhất quán giữa các dự án.
|
Mỗi repo tự quản dependency, khó kiểm soát hơn.
|
|
Có thể dùng cấu hình CI/CD thống nhất, phát hiện mã thay đổi và chỉ chạy build/test bị ảnh hưởng.
|
Mỗi repo có pipeline riêng. Giữ tách biệt nhưng khó hợp nhất.
|
|
Phù hợp cho đội nhỏ hoặc vừa, hoặc dự án có nhiều mã dùng chung.
|
Phù hợp khi dự án độc lập, cần kiểm soát truy cập chặt, hoặc thuộc sở hữu các đội khác nhau.
|
Lợi ích của Monorepo
Monorepo mang lại rất nhiều lợi ích. Dưới đây là một vài điểm:
Tăng cường cộng tác và khả năng quan sát
Khi tất cả mã ở một nơi, ai cũng có thể thấy chuyện gì đang diễn ra. Bạn có thể xem các dự án khác, học hỏi, hoặc hỗ trợ khi cần.
Điều này giúp làm việc nhóm trơn tru, hỗ trợ các đội phối hợp tốt hơn và giảm silo.
Đơn giản hóa quản lý dependency
Nếu nhiều dự án dùng chung thư viện nhưng mỗi nơi giữ một phiên bản, mọi thứ sẽ rối rất nhanh.
Ví dụ, một dự án dùng phiên bản cũ còn dự án khác dùng phiên bản mới. Điều này khiến mã khó chạy hơn, và bạn có thể phải tốn thời gian sửa cùng một lỗi ở nhiều nơi.
Tuy nhiên, trong monorepo, mã dùng chung nằm ở một vị trí. Bạn cập nhật một lần, mọi dự án nhận bản vá ngay. Không phải đuổi theo lỗi lệch phiên bản hay xung đột giữa các phiên bản khác nhau.
Commit nguyên tử và refactor dễ dàng hơn
Đôi khi bạn cần thay đổi ảnh hưởng đến hơn một dự án. Chẳng hạn muốn cập nhật một thư viện dùng chung. Với monorepo, bạn có thể cập nhật mã đó và áp dụng sửa chữa cho tất cả dự án chỉ trong một commit.
Điều này giữ các dự án đồng bộ, giúp bạn không lo vô tình làm hỏng phần mã khác.
Công cụ và quy trình nhất quán
Với một repo, có một cách thống nhất để build, test và deploy. Bạn không phải ghi nhớ thiết lập khác nhau cho từng dự án. Điều này giúp thành viên mới bắt đầu nhanh chóng, không bối rối.
Lặp nhanh hơn cho một số quy trình làm việc
Nếu bạn có hai hoặc nhiều dự án phụ thuộc cùng một mã, ví dụ một thư viện hoặc tiện ích dùng chung, bạn không phải cập nhật từng dự án một trong các kho riêng.
Với monorepo, bạn cập nhật mã một lần, và tất cả dự án trong repo đó nhận thay đổi ngay. Tức là thay vì chuyển qua lại giữa nhiều kho, bạn thay đổi ở một nơi và thấy tác động ở mọi nơi cùng lúc.
Điều này hữu ích trong vài tình huống thường ngày:
- Khi refactor mã dùng chung, bạn cập nhật thư viện, và mọi service phụ thuộc sẽ nhận cập nhật ngay.
- Khi tung ra một tính năng chạm đến hơn một ứng dụng, bạn có thể phát hành đồng thời thay vì phải điều phối giữa nhiều repo.
- Ngay cả pipeline CI/CD cũng đơn giản hơn vì build và test chạy trên cùng codebase thay vì bị nhân đôi.
Tất cả điều này giảm qua lại, giảm chuyển ngữ cảnh, và rút ngắn vòng phản hồi. Nhờ đó, tốc độ lặp được đẩy nhanh mà không thêm chi phí.
Thách thức của Monorepo
Monorepo mang lại nhiều lợi ích, nhưng cũng đi kèm một số thách thức. Dưới đây là các điểm chính:
Vấn đề mở rộng và hiệu năng
Khi repo lớn dần, các tác vụ thường ngày như clone, tìm kiếm, thậm chí mở trong editor bắt đầu chậm lại. Vì sao? Vì Git ban đầu không được xây dựng để xử lý codebase khổng lồ. Do đó các công ty lớn thường cảm nhận áp lực này.
Thời gian build và test dài hơn
Vì tất cả dự án cùng tồn tại tại một nơi, một thay đổi nhỏ có thể kích hoạt nhiều công việc thừa cho pipeline CI/CD. Vì vậy, các lập trình viên phải chờ đợi build hoặc test suite hoàn tất trước khi có thể merge hoặc phát hành mã.
Theo thời gian, những độ trễ này có thể gây thêm vấn đề, như chặn các công việc khác và làm khó việc triển khai thay đổi nhỏ, thường xuyên.
Lấy Uber làm ví dụ: khi monorepo Go của họ đạt đến hàng triệu dòng mã, một số kỹ sư phải chờ hàng giờ để build và xác thực xong trước khi có thể merge.
Sau đó, họ tăng tốc với các công cụ như Bazel và hàng đợi build tùy chỉnh. Tuy vậy, điều này cho thấy CI có thể nhanh chóng trở thành nút thắt cổ chai khi tất cả dự án cùng chia sẻ một kho.
Rủi ro nhánh main bị hỏng
Nhánh main là nhánh mặc định trong Git nơi phiên bản ổn định mới nhất của mã tồn tại. Trong monorepo, mọi người làm việc từ nhánh chung này, nên nếu một thay đổi làm hỏng nó, cả đội (hoặc công ty) có thể bị chặn cho đến khi sửa xong.
Lấy Airbnb làm ví dụ. Họ vận hành triển khai dân chủ (bất kỳ kỹ sư nào cũng có thể tự kiểm thử và đưa vào sản xuất mà không cần chờ quản lý phát hành).
Ban đầu cảm giác rất nhanh, nhưng khi công ty lớn dần, việc quản lý trở nên khó khăn vì toàn bộ mã nằm trong một monorepository lớn. Thành viên đưa thay đổi vào sản xuất đồng thời, các bản cập nhật xung đột, khiến khó tìm nguyên nhân sự cố.
Kiểm soát truy cập và bảo mật
Khó giới hạn truy cập trong monorepo vì toàn bộ mã, file cấu hình, script build và tài nguyên khác được lưu cùng nhau. Theo mặc định, bất kỳ lập trình viên nào có quyền vào repo thường có thể xem và sửa tất cả các thành phần này.
Điều đó phù hợp với hợp tác mở, nhưng là thách thức khi một số phần như khóa bảo mật, script triển khai, hoặc thuật toán sở hữu cần quyền chặt chẽ hơn.
Ví dụ, một công ty có thể giữ mã website trong cùng monorepo. Lập trình viên làm website không nhất thiết cần quyền truy cập thông tin xác thực máy chủ production, nhưng vì mọi thứ ở một nơi, khó ngăn họ vô tình xem hoặc sửa các file nhạy cảm đó.
Đường cong học tập cho lập trình viên mới
Với người mới, một repo đồ sộ chứa nhiều dự án có thể gây choáng. Vì có quá nhiều file và thư mục khiến họ khó biết bắt đầu từ đâu hoặc phần nào quan trọng cho công việc.
Họ cũng cần học cấu trúc dự án, công cụ và dependency trước khi có thể tự tin build hoặc thay đổi, điều này có thể làm chậm tiến độ cho đến khi quen thuộc mọi thứ.
Thực tiễn Tốt nhất để Quản lý Monorepo
Để đảm bảo monorepo vận hành trơn tru, dưới đây là những việc bạn nên làm:
- Giữ repo có tổ chức theo cách hợp lý. Gom các dự án liên quan, đặt tên rõ ràng và giúp bất kỳ ai cũng dễ tìm thứ họ cần.
- Tránh nhánh sống lâu. Thử phát triển dựa trên trunk, nơi mọi người thường xuyên merge thay đổi vào nhánh main. Điều này giữ repo sạch và giảm xung đột.
- Cố định dependency theo phiên bản cụ thể để mọi người đồng bộ. Nâng cấp trên toàn repo cùng lúc để tránh lệch.
- Dùng các công cụ build hiện đại như Bazel, Buck, Nx, Pants, Rush hoặc Lerna để tăng tốc build và xử lý độ phức tạp của repo lớn.
- Đừng rebuild hoặc retest toàn bộ repo nếu chỉ một phần nhỏ thay đổi. Dùng build vi sai và kiểm thử có mục tiêu để tiết kiệm thời gian và đưa phản hồi nhanh hơn.
- Thiết lập Git CODEOWNERS trong nền tảng VCS của bạn (ví dụ GitHub/GitLab) để bảo vệ khu vực nhạy cảm như script triển khai hoặc file cấu hình, và yêu cầu đúng đội ngũ duyệt thay đổi.
Công cụ Phổ biến để Quản lý Monorepo
Monorepo có thể trở nên phức tạp khi dự án mở rộng. Vì vậy, để đơn giản hóa, nhiều công cụ đã được xây dựng để hỗ trợ build, dependency và giữ mọi thứ gọn gàng.
Dưới đây là một số công cụ phổ biến:
Bazel
Bazel là hệ thống build mã nguồn mở do Google tạo ra để xử lý codebase rất lớn và phức tạp.
Một số tính năng chính:
- Build nhanh: Chỉ rebuild phần đã thay đổi, dùng bộ nhớ đệm tiên tiến, phân tích dependency tối ưu và thực thi song song.
- Hỗ trợ đa ngôn ngữ: Hoạt động trên nhiều nền tảng và ngôn ngữ, gồm Java, C++, Go, Android và iOS, và chạy trên Windows, macOS, Linux.
- Mở rộng dễ dàng: Xử lý tốt monorepo khổng lồ và nhiều kho, phù hợp cho tổ chức ở mọi quy mô.
- Khả năng mở rộng: Cho phép bổ sung ngôn ngữ và nền tảng mới thông qua hệ thống mở rộng, được cộng đồng hỗ trợ ngày càng tăng.
Bazel được tin dùng bởi các công ty như Google, Stripe và Dropbox để build và test hạ tầng, ứng dụng quan trọng.
Nx
Nx là bộ công cụ mã nguồn mở để quản lý monorepo và đặc biệt phổ biến trong JavaScript và TypeScript, với hỗ trợ mạnh cho các framework như React, Angular, Vue và NestJS. Nó giúp đội ngũ tổ chức nhiều ứng dụng và thư viện ở một nơi, đồng thời giữ build và test nhanh.
Một số tính năng chính:
- Trình chạy tác vụ thông minh: Hiểu mối phụ thuộc giữa các phần của mã, nên chỉ chạy những gì cần thiết và tránh lãng phí thời gian.
- Tương thích với hiện trạng: Chạy các script sẵn có như npm hoặc tác vụ Gradle, nên bạn không phải thay đổi thiết lập.
- Nx Cloud: Tăng tốc vòng đời phát triển với bộ nhớ đệm từ xa, CI nhanh hơn và công cụ giúp tự động phát hiện, sửa lỗi.
- Công cụ bổ trợ: Tính năng như Nx Console cung cấp tự động hoàn thành, đồ thị dự án trực quan và cách quản lý tác vụ dễ dàng.
Lerna
Lerna là một trong những công cụ sớm nhất và đáng tin cậy để quản lý monorepo JavaScript và TypeScript. Nó giúp dễ dàng tổ chức nhiều package trong một kho và hỗ trợ build, test, xuất bản.
Thực tế, một số dự án nổi tiếng như Create React App, Jest và NestJS đã dùng Lerna để quản lý các package của họ.
Một số tính năng chính:
- Build thông minh: Tránh lặp lại công việc bằng cách tái sử dụng kết quả đã cache thay vì build lại cùng một mã.
- Thực thi nhanh: Tác vụ chạy song song nhưng vẫn tôn trọng thứ tự phụ thuộc.
- Bộ nhớ đệm phân tán: Có thể chia sẻ kết quả build giữa lập trình viên và hệ thống CI, rút ngắn thời gian build.
- Xuất bản package: Đơn giản hóa xuất bản lên npm với cả phiên bản dùng chung và độc lập.
- Khả năng mở rộng: Phân tán khối lượng công việc lên nhiều máy mà không cần cấu hình thêm.
- Trực quan hóa: Đi kèm công cụ hiển thị cách các dự án và dependency kết nối trong repo.
- Thiết lập nhẹ: Cần rất ít cấu hình, nên bạn có thể giữ các script npm hiện có và chạy chúng nhanh hơn.
Pants
Pants là hệ thống build nhanh và thân thiện, phù hợp cho monorepo ở nhiều kích cỡ. Ban đầu tập trung vào Python nhưng giờ hỗ trợ cả Go, Java, Scala, Kotlin, Shell và Docker, và còn bổ sung thêm ngôn ngữ.
Vì vậy, nó được sử dụng rộng rãi và tin cậy bởi Coinbase, IBM, Slack, Salesforce và Orca Security, cũng như nhiều đội ngũ nhỏ.
Một số tính năng chính:
- Dễ áp dụng: Cần cấu hình tối thiểu và có thể suy luận hầu hết thông tin build tự động, nghĩa là ít mã rập khuôn phải duy trì hơn.
- Build an toàn: Hỗ trợ nhiều bộ giải dependency và lockfile, tạo các build có thể tái lập, chống chịu tốt hơn trước tấn công chuỗi cung ứng.
- Linh hoạt: Làm việc ở cấp độ file, nên xử lý được cấu trúc dependency phức tạp hoặc chồng chéo mà không ép buộc tính mô-đun nghiêm ngặt.
- Khả năng mở rộng: Cung cấp hệ thống plugin dùng Python để đội ngũ tùy biến theo nhu cầu riêng.
- Hiểu biết về Git: Phát hiện khác biệt giữa các nhánh và chỉ chạy test hoặc build bị ảnh hưởng.
Rush
Rush là công cụ monorepo thiết kế cho các dự án JavaScript cần quản lý nhiều package trong một kho.
Một số tính năng chính:
- Xây dựng cho quy mô lớn: Hỗ trợ build song song, gia tăng và phân tán để ngay cả repo khổng lồ vẫn hiệu quả.
- Điều phối đội ngũ: Giữ phiên bản dependency nhất quán, duyệt package mới trước khi thêm, và hỗ trợ cả phiên bản dùng chung lẫn độc lập.
- Cài đặt đáng tin cậy: Hỗ trợ pnpm (khuyến nghị), npm và Yarn để việc cài đặt có thể dự đoán.
- Công cụ tất cả trong một: Xử lý cài đặt, liên kết, build, xuất bản, quản lý phiên bản và nhật ký thay đổi tại một nơi.
Rush là nền tảng mã nguồn mở và đáng tin cậy, được dùng bởi các sản phẩm lớn như Azure SDK, HBO Max, OneDrive, SharePoint, Office 365 và Wix.
Turborepo
Turborepo là hệ thống build hiệu năng cao giúp monorepo JavaScript và TypeScript nhanh hơn và dễ quản lý hơn. Nó tập trung giải quyết các vấn đề mở rộng thường gặp khi nhiều ứng dụng và package cùng tồn tại trong một kho.
Một số tính năng chính:
- Bộ nhớ đệm từ xa: Lưu kết quả build và test để không phải chạy lại công việc giống nhau, cắt giảm thời gian CI.
- Lập lịch tác vụ: Chạy tác vụ theo đúng thứ tự và song song trên tất cả nhân xử lý để đạt tốc độ tối đa.
- Áp dụng dần: Có thể thêm vào bất kỳ repo nào trong vài phút, dùng các script package.json hiện có và hoạt động với npm, Yarn hoặc PNPM.
- Clone nhẹ: Cho phép lập trình viên chỉ clone phần họ cần của monorepo, giảm thời gian thiết lập.
- Quy trình tối ưu: Chỉ build phần đã thay đổi, giữ vòng phản hồi nhanh.
- Trải nghiệm quen thuộc: Hoạt động với quy trình Git tiêu chuẩn và không yêu cầu thay đổi cách các đội đang quản lý mã.
Moon
Moon là công cụ quản lý monorepo nhanh được viết bằng Rust. Hỗ trợ các ngôn ngữ như JavaScript, TypeScript, Rust, Go và Ruby.
Một số tính năng chính:
- Tốc độ: Dùng bộ nhớ đệm thông minh và build gia tăng để chỉ build lại mã đã thay đổi.
- Hợp tác: Bộ nhớ đệm từ xa chia sẻ kết quả giữa đồng đội và CI.
- Đa nền tảng: Chạy trên Linux, macOS và Windows.
- Đồ thị dự án: Lập bản đồ dependency để giúp tổ chức và mở rộng repo lớn.
Lage
Lage là trình chạy tác vụ xây dựng cho monorepo, tập trung vào tốc độ và hiệu quả. Nó giúp đội ngũ tránh build lại công việc đã làm.
Một số tính năng chính:
- Bỏ qua công việc lặp lại: Tái sử dụng kết quả cục bộ hoặc từ đồng đội, nên build không chạy hai lần.
- Thiết lập nhanh: Dễ cấu hình và hoạt động trên nhiều môi trường.
- Bộ nhớ đệm: Hỗ trợ bộ nhớ đệm cục bộ hoặc lưu trữ bên ngoài để tăng tốc pipeline CI.
- Thông tin chuyên sâu: Bao gồm công cụ để phân tích hiệu năng build và trực quan hóa đồ thị dependency.
Yarn workspaces
Yarn giới thiệu workspaces để giúp quản lý các dự án có nhiều package bên trong một repo (monorepo) dễ dàng hơn.
Một số tính năng chính:
- Không cài trùng lặp: Dependency chung chỉ được cài một lần ở thư mục gốc, tránh phình to
node_modules ở mỗi package.
- Liên kết tự động: Nếu một package phụ thuộc vào package khác trong cùng repo, Yarn sẽ tự liên kết.
- Một lockfile duy nhất: Có một yarn.lock cho toàn repo, giúp phiên bản nhất quán trên mọi package.
Khi nào nên dùng Monorepo
Hãy xem một vài kịch bản phổ biến khi monorepo sẽ hữu ích cho bạn:
Đội ngũ nhỏ đến vừa với mã dùng chung
Nếu đội của bạn nhỏ và nhiều mã được tái sử dụng giữa các dự án, monorepo có thể tiết kiệm thời gian. Vì mọi người trong đội có thể làm việc cùng một nơi, và thư viện dùng chung luôn đồng bộ mà không tốn công thêm. Một mũi tên trúng hai đích.
Dự án có quan hệ phụ thuộc chặt chẽ
Khi các dự án khác nhau phụ thuộc chặt vào nhau, hãy giữ chúng trong một repo. Như vậy, thay đổi có thể diễn ra cùng nhau trong một commit thay vì xoay trở nhiều repo.
Tổ chức muốn công cụ và khả năng quan sát thống nhất
Với monorepo, bạn có thể thiết lập cấu hình CI/CD thống nhất. Điều này không có nghĩa chỉ có một pipeline cho mọi dự án, nhưng có nghĩa bạn có thể tối ưu pipeline để chúng chỉ chạy build và test bị ảnh hưởng bởi thay đổi mã gần đây, giúp tiết kiệm thời gian và tài nguyên.
Nhưng monorepo không hoàn hảo cho mọi trường hợp. Nếu dự án của bạn hoàn toàn độc lập, hoặc bạn cần kiểm soát truy cập nghiêm ngặt (một số đội không nên thấy một số mã), thì polyrepo (nhiều repo) có thể là lựa chọn phù hợp.
Cân nhắc Ưu và Nhược điểm
Dưới đây là cái nhìn nhanh về ưu và nhược điểm của monorepo:
|
Ưu điểm
|
Nhược điểm
|
|
Dễ cộng tác và quan sát giữa các đội.
|
Repo có thể rất lớn và làm chậm Git/IDE.
|
|
Thư viện dùng chung nhất quán, không xung đột phiên bản.
|
Build và test lâu hơn khi codebase phình to.
|
|
Thực hiện thay đổi xuyên dự án trong một commit.
|
Một thay đổi hỏng có thể chặn tất cả mọi người.
|
|
Một cách thống nhất để build, test và deploy (công cụ nhất quán).
|
Khó thiết lập kiểm soát truy cập chi tiết.
|
|
Phản hồi nhanh hơn khi cập nhật mã dùng chung.
|
Đường cong học tập dốc hơn cho người mới.
|
Kết luận
Monorepo có thể giúp cộng tác dễ dàng hơn, dependency đơn giản hơn và lặp nhanh hơn, nhưng cũng đi kèm thách thức thực sự về quy mô, hiệu năng và truy cập. Quyết định áp dụng nên là lựa chọn chiến lược dựa trên cách đội ngũ được tổ chức, mức độ phụ thuộc lẫn nhau giữa các dự án, và việc bạn sẵn sàng đầu tư vào công cụ phù hợp hay chưa.
Nhưng nếu thiếu thói quen cộng tác tốt và quyền sở hữu rõ ràng, ngay cả thiết lập monorepo tốt nhất cũng sẽ gặp khó. Nếu bạn đang cân nhắc hướng đi này, đáng để mài giũa các nền tảng trước. Khóa học Software Engineering Principles in Python là điểm khởi đầu tuyệt vời. Nó sẽ giúp bạn xây dựng thực hành khiến monorepo thành công.
Bạn đang làm một dự án. Frontend ở một repo, backend ở repo khác, và thư viện dùng chung ở repo thứ ba. Bạn phải liên tục chuyển repo và hy vọng phiên bản không bị lệch nhau.
Giờ hãy tưởng tượng mọi thứ ở cùng một nơi. Một repo cho tất cả dự án của bạn, mã dùng chung được cập nhật ngay lập tức, và thay đổi lan tỏa chỉ trong một commit.
Monorepo biến điều đó thành hiện thực. Đó là lý do nhiều công ty công nghệ lớn cũng sử dụng, như:
Trong hướng dẫn này, bạn sẽ biết monorepo là gì, cách so sánh với polyrepo, cùng các lợi ích, thách thức và thực tiễn tốt nhất cần nắm.
Monorepo là gì?
Monorepo (monolithic repository) nghĩa là một kho lưu trữ duy nhất. Trong monorepo, tất cả các dự án khác nhau của bạn cùng nằm ở một nơi, thay vì tách ra nhiều kho riêng.
Dù các dự án nằm cạnh nhau, chúng vẫn độc lập. Tức là bạn có thể build, test và triển khai một cách độc lập, đồng thời được lợi khi giữ mọi thứ tập trung ở một chỗ.
Nhưng lưu ý: ứng dụng nguyên khối (monolithic application) và monorepo không phải là một:
Monorepo không phải là ứng dụng nguyên khối. Thực tế, monorepo thường phối hợp rất tốt với microservices, vì mỗi service có thể sống trong cùng repo mà vẫn độc lập.
Khái niệm monorepo không mới.
Đầu những năm 2000, các công ty công nghệ lớn bắt đầu áp dụng cách tiếp cận “shared codebase”. Ý tưởng rất đơn giản: thay vì rải mã ra nhiều repo, đưa tất cả vào một chỗ để việc cộng tác dễ dàng hơn.
Kể từ đó, các công ty lớn như Google, Meta, Microsoft, Airbnb, Twitter (X), Uber và Pinterest đã áp dụng monorepo để quản lý codebase lớn và thay đổi nhanh của họ.
Monorepo vs. Polyrepo
Đối lập với monorepo là polyrepo (multirepo). Hãy xem sự khác biệt.
Polyrepo
Polyrepo, còn gọi là multirepo, là khi mỗi dự án có kho lưu trữ riêng biệt.
Ví dụ, mã frontend có thể ở một kho, backend ở kho khác, và thư viện dùng chung ở kho riêng. Mỗi kho được quản lý độc lập với dependency và quy trình riêng.
Monorepo so với polyrepo. Hình minh họa của Tác giả.
Nhìn nhanh sự khác biệt chính
Hãy xem những khác biệt chính giữa monorepo và polyrepo:
Monorepo
Polyrepo
Một kho lưu trữ cho tất cả dự án và mã dùng chung.
Mỗi dự án có kho riêng biệt.
Dễ chia sẻ và tái sử dụng mã vì mọi thứ ở cùng một repo.
Khó chia sẻ mã hơn vì dự án có thể trùng lặp công việc hoặc lệch nhịp.
Quản lý tập trung nên phiên bản nhất quán giữa các dự án.
Mỗi repo tự quản dependency, khó kiểm soát hơn.
Có thể dùng cấu hình CI/CD thống nhất, phát hiện mã thay đổi và chỉ chạy build/test bị ảnh hưởng.
Mỗi repo có pipeline riêng. Giữ tách biệt nhưng khó hợp nhất.
Phù hợp cho đội nhỏ hoặc vừa, hoặc dự án có nhiều mã dùng chung.
Phù hợp khi dự án độc lập, cần kiểm soát truy cập chặt, hoặc thuộc sở hữu các đội khác nhau.
Lợi ích của Monorepo
Monorepo mang lại rất nhiều lợi ích. Dưới đây là một vài điểm:
Tăng cường cộng tác và khả năng quan sát
Khi tất cả mã ở một nơi, ai cũng có thể thấy chuyện gì đang diễn ra. Bạn có thể xem các dự án khác, học hỏi, hoặc hỗ trợ khi cần.
Điều này giúp làm việc nhóm trơn tru, hỗ trợ các đội phối hợp tốt hơn và giảm silo.
Đơn giản hóa quản lý dependency
Nếu nhiều dự án dùng chung thư viện nhưng mỗi nơi giữ một phiên bản, mọi thứ sẽ rối rất nhanh.
Ví dụ, một dự án dùng phiên bản cũ còn dự án khác dùng phiên bản mới. Điều này khiến mã khó chạy hơn, và bạn có thể phải tốn thời gian sửa cùng một lỗi ở nhiều nơi.
Tuy nhiên, trong monorepo, mã dùng chung nằm ở một vị trí. Bạn cập nhật một lần, mọi dự án nhận bản vá ngay. Không phải đuổi theo lỗi lệch phiên bản hay xung đột giữa các phiên bản khác nhau.
Commit nguyên tử và refactor dễ dàng hơn
Đôi khi bạn cần thay đổi ảnh hưởng đến hơn một dự án. Chẳng hạn muốn cập nhật một thư viện dùng chung. Với monorepo, bạn có thể cập nhật mã đó và áp dụng sửa chữa cho tất cả dự án chỉ trong một commit.
Điều này giữ các dự án đồng bộ, giúp bạn không lo vô tình làm hỏng phần mã khác.
Công cụ và quy trình nhất quán
Với một repo, có một cách thống nhất để build, test và deploy. Bạn không phải ghi nhớ thiết lập khác nhau cho từng dự án. Điều này giúp thành viên mới bắt đầu nhanh chóng, không bối rối.
Lặp nhanh hơn cho một số quy trình làm việc
Nếu bạn có hai hoặc nhiều dự án phụ thuộc cùng một mã, ví dụ một thư viện hoặc tiện ích dùng chung, bạn không phải cập nhật từng dự án một trong các kho riêng.
Với monorepo, bạn cập nhật mã một lần, và tất cả dự án trong repo đó nhận thay đổi ngay. Tức là thay vì chuyển qua lại giữa nhiều kho, bạn thay đổi ở một nơi và thấy tác động ở mọi nơi cùng lúc.
Điều này hữu ích trong vài tình huống thường ngày:
Tất cả điều này giảm qua lại, giảm chuyển ngữ cảnh, và rút ngắn vòng phản hồi. Nhờ đó, tốc độ lặp được đẩy nhanh mà không thêm chi phí.
Thách thức của Monorepo
Monorepo mang lại nhiều lợi ích, nhưng cũng đi kèm một số thách thức. Dưới đây là các điểm chính:
Vấn đề mở rộng và hiệu năng
Khi repo lớn dần, các tác vụ thường ngày như clone, tìm kiếm, thậm chí mở trong editor bắt đầu chậm lại. Vì sao? Vì Git ban đầu không được xây dựng để xử lý codebase khổng lồ. Do đó các công ty lớn thường cảm nhận áp lực này.
Thời gian build và test dài hơn
Vì tất cả dự án cùng tồn tại tại một nơi, một thay đổi nhỏ có thể kích hoạt nhiều công việc thừa cho pipeline CI/CD. Vì vậy, các lập trình viên phải chờ đợi build hoặc test suite hoàn tất trước khi có thể merge hoặc phát hành mã.
Theo thời gian, những độ trễ này có thể gây thêm vấn đề, như chặn các công việc khác và làm khó việc triển khai thay đổi nhỏ, thường xuyên.
Lấy Uber làm ví dụ: khi monorepo Go của họ đạt đến hàng triệu dòng mã, một số kỹ sư phải chờ hàng giờ để build và xác thực xong trước khi có thể merge.
Sau đó, họ tăng tốc với các công cụ như Bazel và hàng đợi build tùy chỉnh. Tuy vậy, điều này cho thấy CI có thể nhanh chóng trở thành nút thắt cổ chai khi tất cả dự án cùng chia sẻ một kho.
Rủi ro nhánh main bị hỏng
Nhánh main là nhánh mặc định trong Git nơi phiên bản ổn định mới nhất của mã tồn tại. Trong monorepo, mọi người làm việc từ nhánh chung này, nên nếu một thay đổi làm hỏng nó, cả đội (hoặc công ty) có thể bị chặn cho đến khi sửa xong.
Lấy Airbnb làm ví dụ. Họ vận hành triển khai dân chủ (bất kỳ kỹ sư nào cũng có thể tự kiểm thử và đưa vào sản xuất mà không cần chờ quản lý phát hành).
Ban đầu cảm giác rất nhanh, nhưng khi công ty lớn dần, việc quản lý trở nên khó khăn vì toàn bộ mã nằm trong một monorepository lớn. Thành viên đưa thay đổi vào sản xuất đồng thời, các bản cập nhật xung đột, khiến khó tìm nguyên nhân sự cố.
Kiểm soát truy cập và bảo mật
Khó giới hạn truy cập trong monorepo vì toàn bộ mã, file cấu hình, script build và tài nguyên khác được lưu cùng nhau. Theo mặc định, bất kỳ lập trình viên nào có quyền vào repo thường có thể xem và sửa tất cả các thành phần này.
Điều đó phù hợp với hợp tác mở, nhưng là thách thức khi một số phần như khóa bảo mật, script triển khai, hoặc thuật toán sở hữu cần quyền chặt chẽ hơn.
Ví dụ, một công ty có thể giữ mã website trong cùng monorepo. Lập trình viên làm website không nhất thiết cần quyền truy cập thông tin xác thực máy chủ production, nhưng vì mọi thứ ở một nơi, khó ngăn họ vô tình xem hoặc sửa các file nhạy cảm đó.
Đường cong học tập cho lập trình viên mới
Với người mới, một repo đồ sộ chứa nhiều dự án có thể gây choáng. Vì có quá nhiều file và thư mục khiến họ khó biết bắt đầu từ đâu hoặc phần nào quan trọng cho công việc.
Họ cũng cần học cấu trúc dự án, công cụ và dependency trước khi có thể tự tin build hoặc thay đổi, điều này có thể làm chậm tiến độ cho đến khi quen thuộc mọi thứ.
Thực tiễn Tốt nhất để Quản lý Monorepo
Để đảm bảo monorepo vận hành trơn tru, dưới đây là những việc bạn nên làm:
Công cụ Phổ biến để Quản lý Monorepo
Monorepo có thể trở nên phức tạp khi dự án mở rộng. Vì vậy, để đơn giản hóa, nhiều công cụ đã được xây dựng để hỗ trợ build, dependency và giữ mọi thứ gọn gàng.
Dưới đây là một số công cụ phổ biến:
Bazel
Bazel là hệ thống build mã nguồn mở do Google tạo ra để xử lý codebase rất lớn và phức tạp.
Một số tính năng chính:
Bazel được tin dùng bởi các công ty như Google, Stripe và Dropbox để build và test hạ tầng, ứng dụng quan trọng.
Nx
Nx là bộ công cụ mã nguồn mở để quản lý monorepo và đặc biệt phổ biến trong JavaScript và TypeScript, với hỗ trợ mạnh cho các framework như React, Angular, Vue và NestJS. Nó giúp đội ngũ tổ chức nhiều ứng dụng và thư viện ở một nơi, đồng thời giữ build và test nhanh.
Một số tính năng chính:
Lerna
Lerna là một trong những công cụ sớm nhất và đáng tin cậy để quản lý monorepo JavaScript và TypeScript. Nó giúp dễ dàng tổ chức nhiều package trong một kho và hỗ trợ build, test, xuất bản.
Thực tế, một số dự án nổi tiếng như Create React App, Jest và NestJS đã dùng Lerna để quản lý các package của họ.
Một số tính năng chính:
Pants
Pants là hệ thống build nhanh và thân thiện, phù hợp cho monorepo ở nhiều kích cỡ. Ban đầu tập trung vào Python nhưng giờ hỗ trợ cả Go, Java, Scala, Kotlin, Shell và Docker, và còn bổ sung thêm ngôn ngữ.
Vì vậy, nó được sử dụng rộng rãi và tin cậy bởi Coinbase, IBM, Slack, Salesforce và Orca Security, cũng như nhiều đội ngũ nhỏ.
Một số tính năng chính:
Rush
Rush là công cụ monorepo thiết kế cho các dự án JavaScript cần quản lý nhiều package trong một kho.
Một số tính năng chính:
Rush là nền tảng mã nguồn mở và đáng tin cậy, được dùng bởi các sản phẩm lớn như Azure SDK, HBO Max, OneDrive, SharePoint, Office 365 và Wix.
Turborepo
Turborepo là hệ thống build hiệu năng cao giúp monorepo JavaScript và TypeScript nhanh hơn và dễ quản lý hơn. Nó tập trung giải quyết các vấn đề mở rộng thường gặp khi nhiều ứng dụng và package cùng tồn tại trong một kho.
Một số tính năng chính:
Moon
Moon là công cụ quản lý monorepo nhanh được viết bằng Rust. Hỗ trợ các ngôn ngữ như JavaScript, TypeScript, Rust, Go và Ruby.
Một số tính năng chính:
Lage
Lage là trình chạy tác vụ xây dựng cho monorepo, tập trung vào tốc độ và hiệu quả. Nó giúp đội ngũ tránh build lại công việc đã làm.
Một số tính năng chính:
Yarn workspaces
Yarn giới thiệu workspaces để giúp quản lý các dự án có nhiều package bên trong một repo (monorepo) dễ dàng hơn.
Một số tính năng chính:
node_modulesở mỗi package.Khi nào nên dùng Monorepo
Hãy xem một vài kịch bản phổ biến khi monorepo sẽ hữu ích cho bạn:
Đội ngũ nhỏ đến vừa với mã dùng chung
Nếu đội của bạn nhỏ và nhiều mã được tái sử dụng giữa các dự án, monorepo có thể tiết kiệm thời gian. Vì mọi người trong đội có thể làm việc cùng một nơi, và thư viện dùng chung luôn đồng bộ mà không tốn công thêm. Một mũi tên trúng hai đích.
Dự án có quan hệ phụ thuộc chặt chẽ
Khi các dự án khác nhau phụ thuộc chặt vào nhau, hãy giữ chúng trong một repo. Như vậy, thay đổi có thể diễn ra cùng nhau trong một commit thay vì xoay trở nhiều repo.
Tổ chức muốn công cụ và khả năng quan sát thống nhất
Với monorepo, bạn có thể thiết lập cấu hình CI/CD thống nhất. Điều này không có nghĩa chỉ có một pipeline cho mọi dự án, nhưng có nghĩa bạn có thể tối ưu pipeline để chúng chỉ chạy build và test bị ảnh hưởng bởi thay đổi mã gần đây, giúp tiết kiệm thời gian và tài nguyên.
Nhưng monorepo không hoàn hảo cho mọi trường hợp. Nếu dự án của bạn hoàn toàn độc lập, hoặc bạn cần kiểm soát truy cập nghiêm ngặt (một số đội không nên thấy một số mã), thì polyrepo (nhiều repo) có thể là lựa chọn phù hợp.
Cân nhắc Ưu và Nhược điểm
Dưới đây là cái nhìn nhanh về ưu và nhược điểm của monorepo:
Ưu điểm
Nhược điểm
Dễ cộng tác và quan sát giữa các đội.
Repo có thể rất lớn và làm chậm Git/IDE.
Thư viện dùng chung nhất quán, không xung đột phiên bản.
Build và test lâu hơn khi codebase phình to.
Thực hiện thay đổi xuyên dự án trong một commit.
Một thay đổi hỏng có thể chặn tất cả mọi người.
Một cách thống nhất để build, test và deploy (công cụ nhất quán).
Khó thiết lập kiểm soát truy cập chi tiết.
Phản hồi nhanh hơn khi cập nhật mã dùng chung.
Đường cong học tập dốc hơn cho người mới.
Kết luận
Monorepo có thể giúp cộng tác dễ dàng hơn, dependency đơn giản hơn và lặp nhanh hơn, nhưng cũng đi kèm thách thức thực sự về quy mô, hiệu năng và truy cập. Quyết định áp dụng nên là lựa chọn chiến lược dựa trên cách đội ngũ được tổ chức, mức độ phụ thuộc lẫn nhau giữa các dự án, và việc bạn sẵn sàng đầu tư vào công cụ phù hợp hay chưa.
Nhưng nếu thiếu thói quen cộng tác tốt và quyền sở hữu rõ ràng, ngay cả thiết lập monorepo tốt nhất cũng sẽ gặp khó. Nếu bạn đang cân nhắc hướng đi này, đáng để mài giũa các nền tảng trước. Khóa học Software Engineering Principles in Python là điểm khởi đầu tuyệt vời. Nó sẽ giúp bạn xây dựng thực hành khiến monorepo thành công.