Try free
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

BenefitChallenge
Code sharingBuild complexity
Atomic changesCI time
ConsistencyPermission management
VisibilityTooling 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

  1. Invest in tooling — Nx, Turborepo early
  2. Clear ownership — CODEOWNERS enforced
  3. Affected detection — Only build changed
  4. Remote caching — Essential for scale
  5. 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