DEV Community

Cover image for 6 Turborepo vs Nx Patterns That Cut Monorepo CI Time by 70%
JSGuruJobs
JSGuruJobs

Posted on

6 Turborepo vs Nx Patterns That Cut Monorepo CI Time by 70%

javascript #monorepo #turborepo #nx

Most teams move to a monorepo and see zero speed gains because they copy the README setup and stop. The real wins come from a few configuration patterns that most repos never implement.

Here are 6 concrete Turborepo and Nx patterns that actually reduce CI time and developer wait time in production projects.


1. Upstream Build Dependencies Instead of Global Builds

If your build task does not declare upstream dependencies, your cache graph is wrong.

Before – naive Turborepo setup

{
  "tasks": {
    "build": {
      "outputs": ["dist/**"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

This builds every package independently. If ui depends on shared-types, build order is undefined.

After – dependency aware build graph

{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The ^build ensures dependencies build first. Turborepo now executes in parallel where possible but preserves correctness.

In a 12 package repo, this alone removed 4 to 6 unnecessary rebuilds per CI run.


2. Filter Changed Packages in CI Instead of Building Everything

The biggest monorepo mistake is running full builds on every PR.

Before – full CI run

- run: npx turbo build
- run: npx turbo test
Enter fullscreen mode Exit fullscreen mode

Every package runs. Every time.

After – change based filtering (Turborepo)

- run: npx turbo build --filter='...[HEAD^1]'
- run: npx turbo test --filter='...[HEAD^1]'
Enter fullscreen mode Exit fullscreen mode

Now only changed packages and their dependents run.

Nx equivalent

- run: npx nx affected --target=build
- run: npx nx affected --target=test
Enter fullscreen mode Exit fullscreen mode

On a 20 package workspace, this typically drops CI from 14 minutes to 3 to 5 minutes.

This kind of pipeline optimization compounds with the architectural decisions described in the JavaScript application architecture patterns for scalable systems when your repo grows beyond a single deployable app.


3. Remote Caching for Team-Wide Build Deduplication

Local caching helps one developer. Remote caching helps the entire team.

Before – local only

npx turbo build
Enter fullscreen mode Exit fullscreen mode

Every developer rebuilds the same unchanged packages.

After – remote cache enabled (Turborepo)

npx turbo login
npx turbo link
Enter fullscreen mode Exit fullscreen mode

Now build artifacts are shared.

Nx equivalent

npx nx connect-to-nx-cloud
Enter fullscreen mode Exit fullscreen mode

On a 6 developer team, remote caching reduced cumulative daily build time by roughly 40%. That is not theoretical. That is hours per week returned.


4. TypeScript Project References for Incremental Type Checking

Monorepos with 15 plus TypeScript packages can spend 30 seconds on type checking alone.

Before – flat TypeScript config

{
  "compilerOptions": {
    "outDir": "dist"
  }
}
Enter fullscreen mode Exit fullscreen mode

Every package compiles from scratch.

After – composite + references

{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "outDir": "dist"
  },
  "references": [
    { "path": "../shared-types" }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Then run:

tsc --build
Enter fullscreen mode Exit fullscreen mode

TypeScript now builds in dependency order and caches results. Incremental builds drop from 25 seconds to under 5 in medium sized repos.

Neither Turborepo nor Nx sets this up for you. You must configure it intentionally.


5. Enforce Package Boundaries to Prevent Graph Explosion

Performance degrades when everything imports everything.

Before – no constraints

// libs/ui importing from apps/web
import { something } from '../../apps/web/internal'
Enter fullscreen mode Exit fullscreen mode

This creates circular and cross layer coupling.

After – Nx enforce-module-boundaries

{
  "rules": {
    "@nx/enforce-module-boundaries": [
      "error",
      {
        "depConstraints": [
          { "sourceTag": "type:app", "onlyDependOnLibsWithTags": ["type:lib"] }
        ]
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Now apps cannot be imported into libraries.

Turborepo does not enforce this. You enforce it through ESLint rules or strict folder conventions.

In large workspaces, clean boundaries reduce affected graph size by 20 to 30%, which directly reduces CI scope.


6. Separate Dev Tasks From Cached Tasks

Caching dev servers is useless and slows things down.

Before – dev cached unintentionally

{
  "tasks": {
    "dev": {}
  }
}
Enter fullscreen mode Exit fullscreen mode

Turborepo may try to treat it like a regular task.

After – persistent and uncached

{
  "tasks": {
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Now local development runs cleanly without polluting cache state.

In Nx, you achieve similar separation by ensuring serve targets are not part of affected production pipelines.

This prevents confusing cache invalidations that cause developers to distrust the system.


Turborepo vs Nx for Performance-Critical Workspaces

If you have fewer than 15 packages and strong existing tooling, Turborepo is usually enough. It accelerates what you already have.

If you have 30 plus packages and cross domain imports, Nx often wins because its project graph analyzes actual file imports instead of just package.json dependencies. That finer granularity means fewer false positives in affected builds.

But neither tool magically optimizes your repo. The speed gains come from:

  1. Correct dependency declarations
  2. Aggressive change filtering
  3. Remote caching
  4. Incremental TypeScript builds
  5. Strict package boundaries

If your CI is still running full builds on every push, you do not have a monorepo problem. You have a configuration problem.

Pick one of these patterns and implement it this week. Measure CI before and after. If you cannot show a delta in minutes saved, you are not using your monorepo tooling correctly.

Top comments (0)