As projects grow, managing code across multiple repositories becomes increasingly painful. Shared libraries drift out of sync, dependency updates cascade through repos, and cross-project refactoring requires coordinated pull requests. Monorepos solve these problems by housing multiple projects in a single repository. Here is when they make sense and how to set them up effectively.
When a Monorepo Makes Sense
A monorepo is not universally superior to multiple repositories. It excels when you have multiple packages or applications that share code, when teams need to make atomic changes across project boundaries, or when you want a single source of truth for tooling configuration, linting rules, and TypeScript settings.
Common monorepo use cases we see at Born Digital:
- Frontend and backend together: An API server and its client application sharing types, validation schemas, and API contracts.
- Component libraries: A design system package consumed by multiple product applications, allowing atomic updates across all consumers.
- Microservices: Related services that share utility code, protobuf definitions, or database schemas.
- Multi-platform apps: Web, mobile, and API projects sharing business logic and type definitions.
Tooling: Turborepo, Nx, and Workspaces
The foundation of any JavaScript/TypeScript monorepo is a workspace manager. pnpm workspaces are our default choice — pnpm's content-addressable storage means shared dependencies are stored once on disk regardless of how many packages use them, significantly reducing install times and disk usage compared to npm or Yarn.
On top of workspaces, you need a task runner that understands your project graph. Turborepo is lightweight and fast, using content-aware hashing to skip tasks when inputs have not changed. It supports remote caching so CI builds and teammate machines can share build artifacts. Nx offers more features including generators, dependency graph visualisation, and affected-project detection, but comes with more complexity. For most projects, Turborepo's simplicity wins.
Repository Structure
A well-organised monorepo typically separates applications from shared packages. Applications live in an apps/ directory and represent deployable units — your web frontend, API server, or admin panel. Shared code lives in packages/ and includes UI components, utility functions, configuration presets, and shared types.
Root-level configuration files (ESLint, TypeScript base config, Prettier) establish shared standards, while individual packages can extend or override these as needed. Keep your root package.json lean — it should primarily contain workspace configuration and shared dev dependencies like the task runner itself.
CI/CD Considerations
The biggest challenge with monorepos in CI/CD is avoiding unnecessary work. You do not want to rebuild and redeploy every application when a single package changes. Both Turborepo and Nx provide mechanisms to detect which projects are affected by a given commit and only run tasks for those projects.
Configure your CI pipeline to use remote caching so build artifacts computed by one pipeline run are available to subsequent runs. This can reduce CI times by 50-80% for incremental changes. Ensure your deployment pipeline maps affected projects to their respective deployment targets — a change in the shared UI package should trigger deployments for all consuming applications, while a change isolated to the API should only deploy the API.
Common Pitfalls to Avoid
The most frequent mistake is treating a monorepo as a dumping ground for unrelated projects. A monorepo should contain projects that genuinely benefit from co-location. Unrelated projects with different teams, release cycles, and dependency requirements are better served by separate repositories.
Dependency management requires discipline. Avoid version mismatches for shared dependencies — if two packages use different versions of React, you will encounter subtle bugs. Use your workspace manager's deduplication features and consider a tool like syncpack to enforce version consistency. At Born Digital, we adopt monorepos for projects where the co-location benefits are clear and measurable. The overhead of setting up proper tooling pays for itself within the first month of development through faster iteration and fewer cross-project integration issues.