The standard pipeline stage sequence, what a lead defines vs delegates in pipeline ownership, how branching strategy shapes pipeline triggers, and build-once-promote-everywhere as the correct artifact model.
Published September 23, 2026
build → unit test → static analysis → containerize → integration test → deploy
Each stage exists to catch a DIFFERENT class of problem, as early and cheaply as possible: build catches compilation errors immediately; unit test catches logic errors in isolation, fast; static analysis (linting, the vulnerability scanning from Health Checks & Production Best Practices) catches code-quality and security issues without even running anything; containerize produces the actual deployable artifact (Multi-Stage Builds); integration test catches issues that only surface when components interact for real; deploy ships it. The ordering matters — putting a slow stage (integration tests against a real database) before fast, cheap checks (unit tests, static analysis) wastes time running the expensive stage on code that a cheap check would have already rejected.
Designing a CI/CD pipeline (as opposed to just using one someone else set up) means making real ownership decisions: WHICH stages are mandatory GATES (a failure blocks the pipeline entirely) vs advisory (a warning that doesn't block), what the ROLLBACK path looks like if a deploy stage itself fails partway, and how BRANCH PROTECTION rules tie into pipeline status (requiring a passing pipeline before a PR can merge). Day-to-day script tweaks (adjusting a specific test command, tuning a timeout) are reasonably delegated to whoever's working in that area — the STRUCTURE and GATES are the higher-leverage decisions worth a lead's explicit, deliberate attention, since they're what actually enforces the team's quality bar.
Trunk-based: every commit to main triggers the full pipeline; short-lived feature
branches merge frequently, each PR runs a subset (build+test) before merge
Feature-branch: longer-lived branches, pipeline runs per-branch, a separate merge/release
pipeline runs when merging to main
The pipeline's trigger configuration needs to match the team's actual branching strategy, not fight it — trunk-based development (frequent small merges to a single main branch) wants a FAST, lightweight per-PR pipeline (since it runs constantly) with a more thorough pipeline reserved for main itself; a feature-branch workflow with longer-lived branches can afford a heavier per-branch pipeline since it runs less frequently per branch. Mismatching these (a slow, heavy pipeline on every trunk-based commit) creates real friction that pushes teams toward batching changes and merging less often — undermining the fast-feedback goal CI is supposed to provide.
WRONG: rebuild the artifact separately for staging, then AGAIN for production
— risk: a dependency version silently resolves differently between builds,
meaning what was actually TESTED in staging isn't bit-identical to what ships
RIGHT: build ONE artifact (a container image, tagged with a version), test it in
staging, then PROMOTE that exact same image (re-tag/re-deploy it, no rebuild) to production
Rebuilding an artifact separately for each environment introduces a real, if subtle, correctness risk: even with a pinned Dockerfile, subtle nondeterminism (a floating base-image patch tag, a dependency resolution edge case) can mean the artifact deployed to production isn't EXACTLY what was validated in staging — directly undermining the point of staging validation in the first place. Building once and promoting the identical artifact through each environment (dev → staging → production) guarantees what was tested is bit-for-bit what actually ships — a foundational CI/CD correctness principle, not just an optimization to save build time.
Q: How would you decide which pipeline stages should be BLOCKING gates vs advisory warnings? A: Blocking gates should be reserved for checks where a failure represents a genuine, non-negotiable problem (tests failing, a CRITICAL security vulnerability per Health Checks & Production Best Practices' scanning discussion); advisory warnings suit checks with a real false-positive rate or subjective judgment calls (some linting rules, code coverage percentage) where blocking every merge would create the same alert-fatigue-style friction covered in Alerting Strategy, training people to route around the gate rather than respect it.
Q: Does build-once-promote-everywhere mean environment-specific configuration can't differ? A: No — the ARTIFACT (the container image, the compiled code) stays identical across environments; CONFIGURATION (database connection strings, feature flags, resource limits) is injected separately per environment at deploy time (via ConfigMaps/Secrets from Config & Secrets, or environment variables) — this separation of 'what code runs' from 'how it's configured' is exactly what makes build-once-promote-everywhere both correct and practical.
Q: What's a concrete failure mode from putting integration tests BEFORE unit tests in the pipeline? A: A trivial, easily-caught unit-level bug would only surface after the pipeline has already spent significant time provisioning infrastructure and running slower integration tests — wasting compute and, more importantly, developer wait time on a failure that a 10-second unit test run would have caught immediately; stage ordering by cost/speed is a real efficiency lever, not just a stylistic preference.
Q: How does artifact promotion interact with database schema migrations, which can't simply be 'promoted' the same way? A: Schema migrations need their OWN explicit, versioned, forward-compatible strategy (typically run as a distinct pipeline step, with migrations designed to be backward-compatible with the PREVIOUS artifact version during a rolling deploy) — this is a genuinely harder problem than promoting a stateless artifact, since the database is shared, mutable state that can't simply be 're-promoted' the way an immutable container image can.