Opinion

5 CI Strategies for Taming Your Monorepo

By Sarah Chen · October 24, 2023

Ship code. Not excuses.

The monorepo CI trap: building everything on every commit

You switched to a monorepo to share code and reduce friction, but now your CI pipeline is grinding to a halt. Every push triggers jobs for the entire 40-package codebase, even though you only touched a single file in the billing engine.

This "build everything" approach is the silent killer of developer velocity. It introduces massive latency, inflates infrastructure costs, and creates a culture where developers are terrified of pushing, fearing they’ll block the whole team. The solution isn't to abandon the monorepo; it's to engineer the pipeline to match the scale of your codebase.

1. Affected-path detection

The foundation of any efficient monorepo CI strategy is the ability to identify exactly what changed. Affected-path detection uses git history and semantic versioning to calculate a "delta" of changed packages.

Instead of running the pipeline for src/ or packages/, your CI runner only triggers jobs for packages that have a direct dependency on the changed files. If you modify the UI library, only the apps that consume it run tests. If you change a core utility, only its dependents are recompiled. This reduces the average job size from 30 minutes to 3.

2. Dependency graph-aware ordering

Once you know what needs to run, you need to know the right order. A naive approach runs everything in parallel, which is fine for small repos but creates resource contention and cascading failures in large ones.

A dependency graph-aware strategy orders your pipeline stages topologically. If package A depends on package B, package B's build and test must complete before package A's job starts. This ensures that when you deploy to staging, the underlying infrastructure is already validated, preventing the "it works on my machine" syndrome.

3. Remote caching (with benchmarks)

Even with affected-path detection, you'll still have to rebuild things occasionally. This is where remote caching comes in. By pushing build artifacts (node_modules, compiler outputs, Docker layers) to a shared cache, subsequent runs can skip the heavy lifting entirely.

Graph showing reduction in build time from 45 minutes to 8 minutes using remote caching

Benchmark: Remote Caching Impact

Without Cache: 45 min
With Remote Cache: 8 min 82% faster

4. Runner parallelism budgets

More parallelism isn't always better. If you have 50 jobs running simultaneously, you might saturate your CI runners, causing timeouts and cascading retry loops.

Implement a parallelism budget. This involves capping the number of concurrent jobs based on the number of available runners. If you have 10 runners, launch 8 or 9 jobs at once. Leave one slot open for critical health checks or emergency rollbacks. This prevents your queue from growing indefinitely and keeps your deployment pipeline responsive.

5. Staged promotion gates per service

In a monorepo, you can't rely on a single "green" build to tell you everything is good. You need granular quality gates. Deploy to staging, run smoke tests, verify APIs, and only then promote the image to the production registry.

Launchpad excels here by allowing you to define environment promotions as first-class steps. If the smoke tests fail for the Auth service, the promotion to Production is blocked automatically, ensuring that a broken package in the middle of the graph doesn't break your entire deployment.

The payoff is worth it

Implementing these five strategies doesn't happen overnight, but the payoff is immediate: faster feedback loops, happier developers, and the ability to scale your codebase without the technical debt piling up.

We've seen teams reduce their average pipeline time by 60% just by switching to affected-path detection and remote caching. If you're still running full builds on every commit, you're leaving money and efficiency on the table.

Read our Case Study: Scaling to 10k Commits