Skip to content
6 min read·Lesson 2 of 10

Build, Test, and Deploy Stages

Design the stages of a pipeline: what runs in build, what tests belong where, and how artefacts move through environments.

A pipeline is just a sequence of stages. Designing it well — getting the right things in the right order — is what makes CI/CD feel fast and trustworthy instead of slow and flaky.

A Standard Pipeline Shape

┌────────┐  ┌────────┐  ┌──────┐  ┌──────────┐  ┌─────────┐  ┌────────┐
│ Lint / │→ │ Build  │→ │ Unit │→ │ Package  │→ │ Deploy  │→ │ E2E /  │
│ Static │  │        │  │ test │  │ artefact │  │  to dev │  │ smoke  │
└────────┘  └────────┘  └──────┘  └──────────┘  └─────────┘  └────────┘
                                                      │
                                                      ▼
                                              Deploy to staging
                                                      │
                                                      ▼
                                              Deploy to prod
                                              (manual approval)

Stage 1: Lint and Static Checks

Run before anything else — they're fast (seconds) and catch obvious issues. Examples:

  • Code formatters (Prettier, Black, gofmt)
  • Linters (ESLint, RuboCop, Pylint, golangci-lint)
  • Type checks (TypeScript, mypy)
  • Static security scanners (Semgrep, CodeQL, Bandit)
  • Secrets scanners (gitleaks, trufflehog)

Failing here means a developer pushed without running their pre-commit hook. Fix and re-push.

Stage 2: Build

Compile or assemble the application into the artefact you'll deploy. The "build once" principle says: produce one immutable artefact and promote that exact artefact through every environment. Never rebuild for staging or prod separately — environment differences hide there.

Examples of artefacts:

  • A Docker image tagged with the commit SHA
  • A JAR / WAR file
  • A zipped Lambda package
  • A static site bundle
  • An npm package, Python wheel, or Rust binary

Push the artefact to a registry — ECR, GHCR, Artifactory, Nexus, S3 — so later stages can pull the same byte-for-byte copy.

Stage 3: Tests — The Test Pyramid

LayerCountSpeedWhat it tests
UnitThousandsMillisecondsOne function/class in isolation
IntegrationHundredsSecondsModules together — DB, queue, internal API
E2E / systemDozensMinutesFull user flows through the deployed app

Run unit tests in parallel on every push. Run integration tests after the build succeeds. Run E2E tests against a deployed environment, not in the build job — they need a running system.

Stage 4: Security and Quality Gates

  • SCA — software composition analysis: scan dependencies for known CVEs (Snyk, Dependabot, Trivy)
  • Container scanning — scan the built image for vulnerable base layers
  • SAST — static application security testing (CodeQL, Semgrep)
  • License compliance — make sure no GPL'd code accidentally landed in your proprietary product
  • Coverage thresholds — fail the build if test coverage drops below a target

Treat findings as actionable signals, not noise. If you ignore the scanner today, you'll keep ignoring it tomorrow.

Stage 5: Deploy to Dev / Preview

Every successful build deploys to a development environment automatically. Often each PR also gets its own ephemeral preview environment — Vercel, Netlify, Cloudflare Pages, and Render do this for you; Kubernetes shops use tools like Argo CD with PR-based namespaces.

Reviewers can click a link and exercise the change before merging.

Stage 6: Integration / Smoke Tests on Deployed System

After deploying, run a small, fast suite that validates the system actually came up: a healthcheck, a few key API calls, a single browser flow. Not a full regression suite — just enough to know the deploy didn't smash anything obvious.

If smoke fails, fail the deploy and notify.

Stage 7: Promotion

The same artefact that passed dev gets promoted to staging, then to prod. Promotion is just "redeploy this version to the next environment." Common gating mechanisms:

  • Manual approval in the CI tool (a button click)
  • A protected branch / tag
  • A change-management ticket reference
  • Time windows (no Friday deploys, etc.)

Stage 8: Production Deploy

Production deploys benefit from progressive strategies — blue/green, canary, rolling — covered in detail later. The core idea is: don't replace 100% of instances at once. Instead, route a small fraction of traffic to the new version, watch metrics, and roll forward only if signals are healthy.

Speed Matters

A pipeline that takes 45 minutes is a pipeline developers context-switch out of. Aim for under 10 minutes from push to dev deploy. Tactics:

  • Cache dependencies — node_modules, pip wheels, Gradle cache, Docker layers
  • Parallelize — run linters, types, and tests concurrently
  • Shard tests across multiple runners
  • Skip unnecessary work — if only docs changed, don't build and test the whole app (path filters)
  • Fail fast — fastest checks first, expensive ones later
  • Larger runners for build-heavy steps; smaller ones for lint

Pipeline Anti-Patterns

  • Rebuilding for each environment — same code, different artefact = subtle bugs
  • Snowflake CI server — manually configured, undocumented, terrified to touch
  • Tests that pass locally but fail in CI — usually environment leakage; fix once, never trust later
  • Long-lived feature branches — defeats the "C" in CI
  • "Just rerun the pipeline" culture — flaky tests destroy trust; quarantine and fix them
  • Manual steps in the runbook — every manual step is a future incident

A Simple Mental Model

  1. Lint and type-check (seconds)
  2. Build the artefact once (a few minutes)
  3. Run tests against it (parallelised)
  4. Scan it for CVEs and secrets
  5. Deploy that exact artefact to dev → staging → prod
  6. At each environment, validate before moving on

Build once, test thoroughly, promote with confidence. That's CI/CD.

Key Takeaways

  • Build once, deploy many — produce a single immutable artefact and promote it.
  • Test pyramid: lots of fast unit tests, fewer integration tests, a handful of end-to-end tests.
  • Run cheap, fast checks first — fail fast.
  • Cache dependencies aggressively to keep pipelines under 10 minutes.
  • Promote the same artefact through dev → staging → prod, never rebuild per environment.

Test your knowledge

Try exam-style practice questions to reinforce what you've learned.

Practice Questions →