A slow CI pipeline is a slow team. When builds take 20 minutes, developers context-switch, stack PRs, and stop running CI before pushing. Fast CI is a force multiplier.

Pipeline Architecture

Structure your pipeline in stages with increasing cost and scope:

Stage 1: Lint + Type Check (30s)

Stage 2: Unit Tests (1-2 min)

Stage 3: Build (1-2 min)

Stage 4: Integration Tests (2-3 min)

Stage 5: E2E Tests (3-5 min)

Stage 6: Deploy (1-2 min)

Fail fast. If linting catches a problem in 30 seconds, don’t wait for a 5-minute E2E suite to tell you.

Parallelization

Run independent jobs concurrently:

# GitHub Actions example
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run lint && npm run typecheck

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test

  build:
    runs-on: ubuntu-latest
    needs: [lint, test]
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run build

Lint, test, and other independent checks run in parallel. Build only runs if both pass.

Caching

Cache dependencies aggressively:

- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: npm-${{ hashFiles('package-lock.json') }}
    restore-keys: npm-

This turns a 60-second npm ci into a 5-second cache restore for most runs.

Also cache:

  • Build outputs (Turborepo remote cache)
  • Docker layers
  • Test databases (restore from a snapshot instead of running migrations)

Only Run What Changed

In a monorepo, don’t run every test for every change:

- uses: dorny/paths-filter@v3
  id: changes
  with:
    filters: |
      api:
        - 'packages/api/**'
      web:
        - 'packages/web/**'

- if: steps.changes.outputs.api == 'true'
  run: npm test --workspace=packages/api

Flaky Test Management

Flaky tests erode trust in CI. Address them aggressively:

  1. Quarantine flaky tests. Move them to a separate suite that doesn’t block PRs.
  2. Track flake rate. A test that fails 5% of the time will block 1 in 20 PRs.
  3. Set a time budget. If a test takes over 30 seconds, it’s probably doing too much.
  4. Avoid sleep-based waits. Poll for conditions instead.

Deploy Pipeline

Keep deployments simple and reversible:

  1. Build once, deploy many. The same artifact goes to staging and production.
  2. Canary deployments. Route a small percentage of traffic to the new version first.
  3. Automated rollback. If error rates spike after deploy, roll back automatically.
  4. Deploy frequently. Daily deploys are less risky than weekly deploys.

A good CI pipeline is invisible. Developers push code, and minutes later it’s live — or they know exactly why it isn’t.