Skip to content
TBH
Back to blog
January 9, 20265 min read

Monorepos and Turborepo: When It Makes Sense

We use monorepos for several of our projects - including our AI RAG application. When you have a unified codebase where frontend, backend, and shared components live together, it's significantly easier to work across the stack.

But monorepos aren't for everyone. Here's our practical guide to when it makes sense - and when you should stick to separate repos.

What is a monorepo?

A monorepo is one repository containing multiple projects. Instead of:

company/frontend-repo
company/backend-repo
company/shared-components
company/mobile-app

You have:

company/monorepo
├── apps/frontend
├── apps/backend
├── apps/mobile
└── packages/shared-components

Tools like Turborepo, Nx, and Lerna help manage builds, caching, and dependencies across projects.

When monorepos make sense

1. You have 3+ apps sharing significant code

If your frontend, backend, and mobile app all use the same TypeScript types, UI components, and utility functions - then a monorepo makes sense.

Keyword: significant code. If you share 2-3 small utility functions, it's not enough.

2. You have separate teams working on related projects

Monorepos make it easier to:

  • Make atomic commits across projects
  • Update shared dependencies once
  • See the entire system in one IDE

3. You need consistent tooling

Same linting rules, same test setup, same CI/CD pipeline for all projects.

Real-world example: Our AI platform

We use Turborepo for an enterprise document search platform we're building. The structure looks like this:

project/
├── apps/
│   ├── web/           # Next.js frontend
│   └── worker/        # BullMQ background jobs
├── packages/
│   ├── db/            # Database client and schema
│   ├── documents/     # Chunking and text processing
│   └── shared/        # Types and validation
└── turbo.json

Why it works for us:

  • One TypeScript codebase - Types are shared automatically between frontend and worker
  • Shared packages - packages/documents is used by both web and worker
  • Parallel development - bun run dev starts everything at once
  • Cached builds - Turborepo only builds what changed

When it's overkill

1. You have one product

If you're building one webapp with one backend - why split it up just to put it back together?

A simple Next.js app with API routes is fine. You don't need "apps/web" and "packages/ui" and "packages/config".

2. Your team is under 5 developers

Monorepos solve coordination problems between teams. If you're sitting next to each other, you can just talk.

3. You're starting a new project

Monorepos add complexity from day one:

  • More complicated CI/CD setup
  • Longer build times (without caching)
  • More configuration to maintain

Start simple. Migrate to monorepo when you have the problem, not before.

The alternatives

npm workspaces

If you just want to share code between 2 projects, npm workspaces are often enough:

{
  "workspaces": ["packages/*", "apps/*"]
}

No Turborepo, no Nx - just native npm.

Git submodules

Works technically for read-only dependencies - but rarely a good developer experience. Versioning and CI integration quickly becomes cumbersome.

Package registry

Publish your shared packages to npm (or a private registry). More overhead, but clearer boundaries.

Why we use Turborepo

Turborepo has evolved massively. With v2.7+, it's become a serious tool:

Devtools and visualization

Turborepo's new Devtools let you visually explore your Package Graph and Task Graph. They hot-reload when you change code - fantastic for debugging cache issues.

Intelligent caching

Remote caching via Vercel means builds are shared across your team. One developer builds, everyone else gets a cache hit. On larger projects, this saves hours every day.

Composable configuration

With extends, packages can inherit configuration from each other. No more copy-pasting tsconfig and eslint rules.

Yarn/npm/pnpm/Bun support

Works with all package managers. We use Bun - it works perfectly.

Turborepo vs Nx

Both have evolved significantly in 2025. Here's an honest comparison:

FeatureTurborepoNx
Setup time5 minutes15-30 minutes
Remote cachingFree (Vercel)Paid (Nx Cloud)
Graph visualizationDevtoolsNx Graph (redesigned)
Package managersAllAll
Enterprise featuresBasicConformance, Polygraph
AI integrationNoneCursor/Copilot config
Polyglot (Java, .NET)JS/TS onlyMaven, Gradle, .NET
Rust-powered coreNoPartially

Our choice: Turborepo

We use Turborepo because:

  • Simplicity - Less configuration, faster start
  • Free caching - Vercel integration without extra costs
  • Perfect for JS/TS - Our primary stack

When Nx is better

  • Enterprise monorepos with 50+ projects
  • Polyglot workspaces (Java, .NET, Gradle)
  • Advanced CI with self-healing and conformance
  • AI-assisted development with built-in agent configuration

The hidden costs

1. CI/CD complexity

You now need to handle:

  • Which projects should be built for which changes?
  • Build caching
  • Test parallelization

2. IDE performance

Large monorepos can slow down your IDE. TypeScript language server has to index much more code.

3. Onboarding

New developers need to understand the entire monorepo structure before they can make their first PR.

Conclusion

Monorepos are a powerful tool - for those who need them. Most don't.

Ask yourself:

  • Am I really sharing that much code between projects?
  • Do I have coordination problems between teams?
  • Is my current structure actually a problem?

If the answer is no, keep your separate repos. It's not old-fashioned - it's pragmatic.

See what we have built

Nordvec, nævn.dk, Matematik i Måneby in the browser, Semantika and open source.

See the projects