Monorepos are back with force. Companies like Google and Meta have used them for decades, but modern tooling, Turborepo, Nx, pnpm workspaces, has made the approach accessible to smaller teams. The question is: should it be accessible to your team?

It depends on real problems you have right now, not theoretical problems you might have in the future.

What monorepos solve well

Code sharing without friction

When you have a design system, a utils library, shared types, or configurations that multiple projects use, a monorepo eliminates the versioning dance: you don't need to publish a package for another project to use the change. It's a direct import.

// In any package in the monorepo
import { Button } from '@company/ui'
import { formatDate } from '@company/utils'
import type { User } from '@company/types'

Atomic refactoring

When you change the interface of a shared function, you update all consumers in the same commit. No breaking changes, no versioning pain, no outdated projects using old APIs for months.

Smarter CI/CD with incremental builds

Tools like Turborepo and Nx detect which packages were modified and only run the affected tests/builds. In large monorepos, this transforms 20-minute pipelines into 3-minute pipelines.

# Turborepo: runs only what changed
turbo run build test --filter='...[HEAD^1]'

What monorepos don't solve

Monorepos don't make bad code better. If you have two projects with inconsistent architecture, putting them in the same repository won't align them, it will expose conflicts faster.

Monorepos don't replace team conventions. Code sharing only works if conventions are shared too. Without that, you have coupling without alignment.

When NOT to use a monorepo

  • You have 1-2 projects with no shared code
  • Projects have completely independent deploy cycles and no domain overlap
  • Your team has no experience with the tooling, the learning cost is real
  • You're in a product phase where individual project velocity matters more than cross-project consistency

Turborepo vs Nx: the practical decision

Turborepo

Best for: teams that want fast setup, primarily JavaScript/TypeScript, focused on incremental builds and tasks. Simple API, minimal configuration, integrates well with Vercel.

// turbo.json: minimal working configuration
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"]
    },
    "test": {
      "dependsOn": ["build"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

Nx

Best for: larger monorepos, polyglot (multiple languages), teams that need code generation, dependency graph visualization, and plugin capabilities. More power, more configuration.

# Nx: generates a new project in the monorepo
nx generate @nx/react:application my-app

# Visualizes the dependency graph
nx graph

Structure of a well-organized monorepo

my-monorepo/
├── apps/
│   ├── web/           # Next.js app
│   ├── mobile/        # React Native
│   └── api/           # Node.js backend
├── packages/
│   ├── ui/            # Shared design system
│   ├── config/        # ESLint, TypeScript, etc.
│   ├── utils/         # Utility functions
│   └── types/         # Shared types
├── turbo.json
├── pnpm-workspace.yaml
└── package.json

The golden rule: apps/ contains things you deploy. packages/ contains things you import. No package should depend on an app, but apps can depend on any package.

The decision

If you have or are about to have multiple projects with shared code, teams working across more than one project, and the need for atomic changes across projects, a monorepo probably justifies the investment.

If none of those apply, you're trading simplicity for complexity without proportional gain. Polyrepo works well for many organizations.

The trap to avoid: adopting a monorepo because "big companies do it" or because it seems more professional. The decision should always be about real problems you have today.