6 min read • Guide 361 of 877
Monorepo Management Strategies
Monorepos house multiple projects in a single repository. Done well, they simplify sharing and consistency. Done poorly, they become slow, confusing, and hard to maintain. This guide covers practical approaches to monorepo management.
Monorepo Trade-offs
| Benefit | Challenge |
|---|---|
| Code sharing | Build complexity |
| Atomic changes | CI time |
| Consistency | Permission management |
| Visibility | Tooling required |
Repository Structure
Organizing the Monorepo
MONOREPO STRUCTURE
══════════════════
TYPICAL STRUCTURE:
─────────────────────────────────────
monorepo/
├── apps/ # Applications
│ ├── web/ # Web frontend
│ ├── api/ # Backend API
│ ├── mobile/ # Mobile app
│ └── admin/ # Admin dashboard
├── packages/ # Shared libraries
│ ├── ui/ # UI components
│ ├── utils/ # Utilities
│ ├── config/ # Shared config
│ └── types/ # Type definitions
├── tools/ # Build tools
│ └── scripts/
├── package.json # Root package
├── nx.json / turbo.json # Monorepo tool config
└── README.md
NAMING CONVENTIONS:
─────────────────────────────────────
Packages:
├── @org/web (apps)
├── @org/api (apps)
├── @org/ui (packages)
├── @org/utils (packages)
├── Scoped naming
└── Clear ownership
OWNERSHIP:
─────────────────────────────────────
Define owners:
├── apps/web → Web Team
├── apps/api → Backend Team
├── packages/ui → Design Systems Team
├── packages/utils → Platform Team
├── CODEOWNERS file
└── Clear responsibility
Dependency Management
Internal Dependencies
DEPENDENCY MANAGEMENT
═════════════════════
INTERNAL DEPENDENCIES:
─────────────────────────────────────
apps/web/package.json:
{
"dependencies": {
"@org/ui": "workspace:*",
"@org/utils": "workspace:*"
}
}
Using workspace protocol:
├── Links to local package
├── No version management
├── Always uses current
├── Atomic updates
└── Simple internal deps
DEPENDENCY GRAPH:
─────────────────────────────────────
┌─────────┐
│ apps │
└────┬────┘
│ depends on
┌────▼────┐
│packages │
└────┬────┘
│ may depend on
┌────▼────┐
│packages │
└─────────┘
Rules:
├── Apps depend on packages
├── Packages can depend on packages
├── No circular dependencies
├── Packages don't depend on apps
└── Clear hierarchy
EXTERNAL DEPENDENCIES:
─────────────────────────────────────
Strategies:
├── Hoist to root (same version)
├── Or allow per-package versions
├── Consistency preferred
├── Regular update cadence
└── Dependency health
HOISTING EXAMPLE:
─────────────────────────────────────
Root package.json:
{
"devDependencies": {
"typescript": "5.0.0",
"jest": "29.0.0",
"eslint": "8.0.0"
}
}
├── All packages use same version
├── Single lock file
├── Consistent tooling
└── Simpler updates
Build System
Efficient Builds
BUILD SYSTEM
════════════
INCREMENTAL BUILDS:
─────────────────────────────────────
Only build what changed:
├── Detect affected packages
├── Build only those + dependents
├── Cache results
├── Reuse unchanged
└── Fast feedback
EXAMPLE (Nx):
─────────────────────────────────────
# Build only affected
nx affected:build
# Test only affected
nx affected:test
# Lint only affected
nx affected:lint
REMOTE CACHING:
─────────────────────────────────────
├── Cache build artifacts
├── Share across team
├── CI cache hit
├── Massive time savings
├── Nx Cloud, Turborepo Remote Cache
└── Essential for scale
BUILD GRAPH:
─────────────────────────────────────
nx graph
Shows:
├── Package dependencies
├── Build order
├── Affected by change
├── Visual understanding
└── Dependency clarity
PARALLEL EXECUTION:
─────────────────────────────────────
├── Build independent packages in parallel
├── Respect dependency order
├── Utilize all cores
├── Faster CI
└── Concurrent where possible
CI/CD
Pipeline Configuration
CI/CD FOR MONOREPOS
═══════════════════
AFFECTED DETECTION:
─────────────────────────────────────
# Only run for affected
- name: Build affected
run: npx nx affected:build --base=main
- name: Test affected
run: npx nx affected:test --base=main
Benefits:
├── PR only builds changed code
├── Fast feedback
├── Less CI time
└── Efficient resources
CACHING:
─────────────────────────────────────
# Cache node_modules
- uses: actions/cache@v3
with:
path: node_modules
key: deps-${{ hashFiles('pnpm-lock.yaml') }}
# Cache build outputs
- uses: actions/cache@v3
with:
path: dist
key: build-${{ github.sha }}
PARALLEL JOBS:
─────────────────────────────────────
jobs:
build-web:
if: needs.changes.outputs.web == 'true'
build-api:
if: needs.changes.outputs.api == 'true'
build-ui:
if: needs.changes.outputs.ui == 'true'
├── Parallel per-package
├── Only if affected
├── Fast CI
└── Efficient
DEPLOY STRATEGY:
─────────────────────────────────────
├── Independent app deploys
├── Only deploy changed apps
├── Packages don't deploy (published)
├── Clear deploy triggers
└── Targeted deployments
Team Practices
Working Together
TEAM PRACTICES
══════════════
CODE OWNERSHIP:
─────────────────────────────────────
CODEOWNERS file:
/apps/web/ @web-team
/apps/api/ @backend-team
/packages/ui/ @design-systems
/packages/utils/ @platform-team
├── Required reviews
├── Clear ownership
├── Right reviewers
└── Accountability
BRANCH STRATEGY:
─────────────────────────────────────
├── Feature branches from main
├── PRs reviewed by owners
├── Main always deployable
├── No long-lived branches
└── Trunk-based development
CHANGE COORDINATION:
─────────────────────────────────────
For cross-package changes:
├── Single PR (atomic)
├── Multiple owners review
├── Test all affected
├── Deploy together
└── Coordinated change
DOCUMENTATION:
─────────────────────────────────────
├── Root README: Overview
├── Each package: Own README
├── Architecture docs
├── Contribution guide
└── Findable documentation
GitScrum Integration
Tracking Work
GITSCRUM FOR MONOREPOS
══════════════════════
PROJECT ORGANIZATION:
─────────────────────────────────────
Options:
├── Single project (whole monorepo)
├── Project per app
├── Team-based projects
├── Choose based on org structure
└── What makes sense for you
LABELS:
─────────────────────────────────────
├── app:web
├── app:api
├── package:ui
├── package:utils
├── cross-package
└── Clear categorization
CROSS-PACKAGE TASKS:
─────────────────────────────────────
Task: "Add dark mode support"
├── packages/ui: Dark theme
├── apps/web: Theme switcher
├── Linked sub-tasks
├── Track together
└── Atomic feature
Best Practices
For Monorepos
- Invest in tooling — Nx, Turborepo early
- Clear ownership — CODEOWNERS enforced
- Affected detection — Only build changed
- Remote caching — Essential for scale
- Consistent standards — Shared config
Anti-Patterns
MONOREPO MISTAKES:
✗ No incremental builds
✗ No caching
✗ Unclear ownership
✗ Circular dependencies
✗ Everything in CI every time
✗ No shared tooling config
✗ Giant PRs across all packages
✗ Ignoring performance