Monorepo vs Multi-repo
A comprehensive guide to repository strategies - choosing between monorepo and multi-repo architectures for your projects
What is a Monorepo?
A Monorepo (monolithic repository) is a single repository that contains multiple projects, applications, or packages. All code lives in one place, with shared tooling, dependencies, and configurations.
Example: Google, Meta, and Microsoft use monorepos containing thousands of projects and millions of lines of code.
What is Multi-repo?
A Multi-repo (polyrepo) strategy uses separate repositories for each project, service, or package. Each repo is independently versioned, deployed, and maintained.
Example: Netflix uses separate repositories for each microservice, with clear ownership boundaries.
Architecture Comparison
| Aspect | Monorepo | Multi-repo |
|---|---|---|
| Code Location | Single repository | Separate repositories |
| Dependencies | Shared, always in sync | Versioned via package manager |
| Tooling | Unified build/test/lint | Per-repo configuration |
| Visibility | Everyone sees all code | Access controlled per repo |
| Atomic Changes | Cross-project changes in one commit | Requires coordinated PRs |
| CI/CD | Complex (need smart filtering) | Simple per-repo pipelines |
Monorepo Deep Dive
Structure
my-company/
├── apps/
│ ├── web/ # Main web application
│ ├── mobile/ # React Native app
│ └── admin-dashboard/ # Internal admin tool
├── packages/
│ ├── ui/ # Shared UI components
│ ├── utils/ # Common utilities
│ ├── config/ # Shared configurations
│ └── types/ # TypeScript definitions
├── services/
│ ├── api-gateway/ # API Gateway service
│ ├── user-service/ # User management
│ └── payment-service/ # Payment processing
├── tools/
│ └── scripts/ # Build and deployment scripts
├── package.json # Root package.json
├── turbo.json # Turborepo config
└── nx.json # Nx config (alternative)Key Benefits
1. Atomic Changes
Make cross-project changes in a single commit.
# One commit updates everything
feat: add dark mode support
- packages/ui: Add theme prop to Button
- apps/web: Implement theme toggle
- services/api: Add user preference endpoint2. Shared Dependencies
Single source of truth for all dependencies.
// Root package.json
{
"dependencies": {
"react": "^18.2.0",
"typescript": "^5.0.0"
}
}No version conflicts. Update once, applies everywhere.
3. Code Reuse
Import shared code directly without publishing packages.
// apps/web/src/components/Header.tsx
import { Button } from '@company/ui';
import { formatDate } from '@company/utils';
import type { User } from '@company/types';4. Unified Tooling
One configuration for linting, testing, and building.
// Root eslint.config.js applies to all projects
// Root tsconfig.json shared across all TypeScript code
// Single CI/CD pipeline with smart cachingMonorepo Tooling
| Tool | Best For | Key Features |
|---|---|---|
| Turborepo | JavaScript/TypeScript projects | Fast, simple, Vercel integration |
| Nx | Enterprise monorepos | Plugins, generators, graph visualization |
| Bazel | Large-scale, multi-language | Hermetic builds, Google-proven |
| Lerna | Publishing npm packages | Versioning, changelog generation |
| pnpm | Efficient package management | Strict dependencies, disk efficient |
Task Orchestration
// turbo.json
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": []
},
"lint": {
"outputs": []
}
}
}Affected Detection
Only build/test what changed:
# Turborepo
npx turbo run build --filter=...[origin/main]
# Nx
npx nx affected --target=build --base=origin/mainMulti-repo Deep Dive
Structure
github.com/my-company/
├── frontend-web/ # Main web application
├── frontend-mobile/ # React Native app
├── backend-api-gateway/ # API Gateway
├── service-users/ # User service
├── service-payments/ # Payment service
├── lib-ui-components/ # Shared UI (npm package)
├── lib-common-utils/ # Shared utilities (npm package)
└── infra-terraform/ # Infrastructure as codeKey Benefits
1. Clear Ownership
Each repo has defined owners and access controls.
# CODEOWNERS in service-payments/
* @payments-team
/src/fraud/ @security-team2. Independent Lifecycles
Each service versions and deploys independently.
3. Simpler CI/CD
Each repo has its own straightforward pipeline.
# .github/workflows/ci.yml (per repo)
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
- run: npm run build4. Technology Flexibility
Each repo can use different languages, frameworks, and tools.
service-users/ → Go + PostgreSQL
service-payments/ → Java + Spring Boot
service-analytics/ → Python + FastAPI
frontend-web/ → React + TypeScriptMulti-repo Challenges & Solutions
Challenge 1: Dependency Management
Solutions:
- Semantic versioning with strict ranges
- Automated dependency updates (Dependabot, Renovate)
- Regular synchronization releases
Challenge 2: Cross-repo Changes
# Updating shared library requires multiple PRs
1. PR to lib-ui-components (bump version)
2. Publish new npm version
3. PR to frontend-web (update dependency)
4. PR to frontend-mobile (update dependency)
5. PR to admin-dashboard (update dependency)Solutions:
- Automation scripts for multi-repo updates
- Codemods for breaking changes
- Versioned contracts (API specs, TypeScript types)
Challenge 3: Code Discovery
Solutions:
- Central documentation (Backstage, wiki)
- Consistent naming conventions
- Service catalog with ownership info
Hybrid Approaches
Many organizations use a combination of both strategies.
Common Patterns
| Pattern | Description |
|---|---|
| Frontend Monorepo | All frontend apps + shared UI in one repo |
| Backend Multi-repo | Each microservice in its own repo |
| Shared Libs Monorepo | Types, contracts, and configs in dedicated monorepo |
| Team Monorepos | Each team owns a monorepo for their domain |
Decision Framework
Choose Monorepo When
- ✅ High code sharing between projects
- ✅ Frequent cross-project changes
- ✅ Single technology stack
- ✅ Small to medium team size
- ✅ Strong DevOps capabilities for tooling
Choose Multi-repo When
- ✅ Clear service boundaries with minimal sharing
- ✅ Teams need strong autonomy
- ✅ Different technology stacks per service
- ✅ Strict access control requirements
- ✅ Simple CI/CD is a priority
Migration Strategies
Monorepo → Multi-repo
# Extract service to new repo
1. Create new repository
2. Copy service code with git history (git filter-branch)
3. Update CI/CD for new repo
4. Remove code from monorepo
5. Update dependencies to use published packagesMulti-repo → Monorepo
# Merge repos while preserving history
1. Create monorepo structure
2. Use git subtree or custom scripts to import repos
3. Update import paths
4. Consolidate configurations
5. Set up monorepo tooling (Nx, Turborepo)Summary Table
| Factor | Monorepo | Multi-repo |
|---|---|---|
| Code Sharing | Easy, direct imports | Via versioned packages |
| Atomic Changes | Single commit across projects | Coordinated PRs |
| CI/CD Complexity | High (needs smart tooling) | Low (simple per-repo) |
| Onboarding | Clone once, access all | Clone multiple repos |
| Access Control | Harder to restrict | Fine-grained per repo |
| Tooling Required | Nx, Turborepo, Bazel | Standard git workflows |
| Scaling | Needs optimization at scale | Scales naturally |
| Dependency Sync | Always in sync | Version management overhead |
Interview Tips
- Know the trade-offs: Neither approach is universally better—it depends on team size, code sharing needs, and organizational structure.
- Discuss tooling: Mention Nx, Turborepo, or Bazel for monorepos; discuss versioning strategies for multi-repo.
- Real-world examples: Google, Meta, Microsoft use monorepos; Netflix, Amazon prefer multi-repo (for microservices).
- Hybrid is common: Many companies use both—monorepo for related projects, multi-repo for independent services.
- Scaling challenges: Discuss how monorepos need affected detection and remote caching at scale.
- Connect to microservices: Multi-repo aligns well with microservices; monorepo works better for shared libraries and related apps.
Repository strategy is a foundational architectural decision. Choose based on your team's collaboration patterns, code sharing needs, and operational capabilities—not industry trends.