Command Palette

Search for a command to run...

Hectal
PHASE 8Advanced ~7 min· topic 2 of 4

Topic 8.2

CI for Monorepos: Path Filters, Affected Builds & Remote Caches

In one line

In a monorepo, rebuilding and testing everything on every change doesn't scale. Path filters, dependency-aware 'affected' builds, and shared remote build caches keep pipelines fast while preserving the benefits of one repository.

0/4 · 0%

Think of it like this

A large office building. When one floor's lighting is changed, you don't inspect the wiring of every floor, only that floor and anything wired through it. Monorepo CI needs the same map of what depends on what.

Key ideas

  1. 01

    PATH FILTERS (simplest): run the checkout-service workflow only when services/checkout/** or shared libs/** change (on.push.paths in GitHub Actions, rules: changes: in GitLab). Easy, but you must remember to list shared dependencies, or changes slip through untested.

  2. 02

    AFFECTED BUILDS (dependency graph): build tools know the dependency graph between projects and compute exactly what a change affects: Nx (nx affected -t test), Turborepo, Bazel (bazel test //... with remote caching), Gradle (build cache + configuration on demand), Maven (-pl -am). Change a shared library and everything that depends on it is rebuilt and retested; nothing else is.

  3. 03

    REMOTE BUILD CACHE: if an output was already built for exactly the same inputs (by another developer or CI run), download it instead of rebuilding: Gradle build cache, Bazel remote cache, Nx Cloud/Turborepo remote cache. Combined with affected builds, typical PR times drop from tens of minutes to a few.

  4. 04

    Other monorepo CI concerns: a merge queue to keep main green (Git course, Phase 8), clone strategies (partial/sparse clones: Git course, Phase 8), CODEOWNERS per directory, and one versioning and release policy per deployable (Topic 8.3).

Code & diagrams

path filters + affected buildsyaml
on:
  pull_request:
    paths: ["services/checkout/**", "libs/**", "build.gradle.kts"]
jobs:
  checkout:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
        with: { fetch-depth: 0 }                       # needed to compare with main
      - uses: actions/setup-java@v5
        with: { distribution: temurin, java-version: "21" }
      - uses: gradle/actions/setup-gradle@v4            # Gradle build cache
      - run: ./gradlew :services:checkout:check --build-cache
what 'affected' meansdiagram
Rendering diagram…

Explain it without notes

01

Why are path filters alone risky in a monorepo?

Practice

01

Your monorepo has 40 services; every PR runs all tests (50 minutes). What do you change first?

Trade-offs

  • ↔

    Graph-aware build tools (Bazel, Nx) give the fastest, most correct pipelines but add a learning curve and strict build definitions; path filters are simple but error-prone.

Done when you can

  • I can set up affected builds and a remote cache

  • My monorepo CI time grows with change size, not repo size