The CI/CD Delivery Pipeline
CI/CD is often said as one word, but it names two separate questions a team needs automated answers to: is this change safe to merge, and is this change safe to release.
Search across all documentation pages
CI/CD is often said as one word, but it names two separate questions a team needs automated answers to: is this change safe to merge, and is this change safe to release.
Continuous Integration (CI) answers the first question by running fast, repeatable checks on every proposed change.
Continuous Delivery/Deployment (CD) answers the second by turning a merged change into a deployable artifact and moving that exact artifact through environments toward production.
CI/CD Basics shows what both pipelines look like as GitHub Actions workflows for a Node service; this page is the model underneath them - what a pipeline is actually for, and why splitting it in two is the right shape rather than an arbitrary convention.
Before CI/CD, "does this work" was answered by a person running tests locally, and "is this safe to release" was answered by another person clicking through a deploy by hand.
Both steps were real work, both were skippable under deadline pressure, and both produced different results depending on who ran them and what state their machine happened to be in.
A pipeline replaces that with a fixed, automated sequence of steps that runs the same way every time, on the same infrastructure, regardless of who triggered it.
A gate is any step in that sequence that can stop the pipeline outright - a failing test, a lint error, a security scan finding a critical vulnerability - and the whole point of a gate is that it blocks by default, rather than relying on someone remembering to check.
The CI/CD split maps onto two genuinely different concerns: CI runs on every proposed change (a pull request) to answer "is this safe to merge," while CD runs after a change is accepted to answer "is this safe to release" - conflating them into one pipeline tends to either slow down every PR with deploy-related steps it doesn't need, or skip release-specific checks that only matter once code is headed to production.
A CI pipeline's job is narrow and fast: install dependencies, then run the cheapest gates first - lint, typecheck - before the more expensive ones - unit tests, and sometimes integration tests - so a broken change fails in seconds rather than minutes.
PR opened
-> install (npm ci)
-> lint (fails fast, seconds)
-> typecheck (fails fast, seconds)
-> unit tests (minutes)
-> merge blocked until all gates pass
A CD pipeline's job starts where CI's ends: take an already-validated change and produce one artifact - a Docker image, a Lambda zip, a compiled bundle - tagged immutably, usually by the git commit SHA that produced it.
That artifact is then promoted: deployed to a staging environment, smoke-tested, and only then deployed to production, with the critical rule that it is the same artifact moving forward at each step, never a fresh build per environment.
Merge to main
-> build artifact, tag with git SHA
-> deploy to staging
-> automated smoke test against staging
-> (approval, or automatic if fully continuous)
-> deploy same artifact to production
Rebuilding per environment defeats the purpose of the whole model: if staging and production are built from separate builds, a passing staging test no longer guarantees anything about what actually reaches production, because the two were never provably the same code.
// A pipeline step tags with the commit SHA, not a mutable label -
// this is what makes "promote the same artifact" possible at all
const image = `ghcr.io/acme/api:${process.env.GITHUB_SHA}`;
// staging and production deploy steps reference this exact tag,
// never `:latest`, so both environments run provably identical codeEnvironments (ci, staging, production) exist to catch different classes of problem at different points: CI catches code-level regressions before merge, staging catches integration and configuration problems against production-like infrastructure, and production is where real traffic finally validates the release.
Rollback is the safety valve this whole model exists to make cheap: because every release is a tagged, immutable artifact, undoing a bad release means redeploying the previous known-good tag, not reconstructing what used to be running from memory.
Continuous delivery and continuous deployment are often used interchangeably but describe different levels of automation: delivery means every change that passes all gates is ready to deploy at any time, typically with a manual approval before production; deployment means every change that passes all gates actually deploys automatically, with no human in the loop at all.
Most teams land somewhere between the two - automatic staging deploys, but a required approval (or a progressive rollout) gating production - because full continuous deployment demands a level of gate coverage and monitoring maturity that takes time to earn.
Monorepos complicate the pipeline model directly: running every gate on every change regardless of what changed doesn't scale once a repository holds many independently deployable services, which is why path-based filtering and tools like Nx or Turborepo that detect "affected" packages have become standard rather than optional at that scale.
Security has become a first-class pipeline concern rather than an afterthought: dependency auditing, container image scanning, and commit-signing/provenance checks now run as gates alongside tests, because a pipeline that only validates functional correctness leaves a real gap for supply-chain risk.
Progressive delivery techniques - canary releases, blue-green deploys - extend the promotion model further by making "deploy to production" itself a gated, incremental process rather than an instant all-or-nothing switch, so a bad release can be caught and rolled back after reaching a small fraction of traffic instead of everyone.
| Delivery Model | Strength | Weakness | Best Fit |
|---|---|---|---|
| Continuous Delivery (manual gate to prod) | Human judgment on release timing; simpler mental model | Slower release cadence; approval can become a bottleneck | Regulated environments, high-risk changes |
| Continuous Deployment (fully automatic) | Fastest possible feedback loop; no release-day ceremony | Requires very high confidence in automated gates | Mature teams with strong test/monitoring coverage |
| Manual release process | No pipeline investment required | Inconsistent, slow, doesn't scale past a tiny team | Early prototypes, throwaway projects |
CI runs on every proposed change to answer "is this safe to merge" - lint, typecheck, tests. CD runs after a change is accepted to answer "is this safe to release" - building an artifact and promoting it through environments toward production.
They serve different moments in a change's lifecycle and different audiences: every contributor's PR needs fast CI feedback, but not every PR needs a release pipeline run. Splitting them keeps PR feedback fast and keeps release-specific steps (building artifacts, deploying) out of the merge path.
Any automated check that can block the pipeline from proceeding - a failing test, a lint violation, a critical vulnerability finding. Gates block by default, which is what makes them more reliable than relying on someone remembering to check manually.
If staging and production are built separately, a passing staging test no longer guarantees anything provable about what actually runs in production, because the two builds were never confirmed to be identical. Tagging one artifact by commit SHA and promoting that same tag preserves that guarantee.
Because every release is a tagged, immutable artifact, rolling back means redeploying the previous known-good tag rather than trying to reconstruct a prior state from memory or manual steps.
Continuous delivery means every change that passes all gates is ready to deploy at any time, typically behind a manual approval. Continuous deployment removes that manual step entirely - every passing change deploys automatically.
Usually yes - running every gate on every change stops scaling once a repo holds multiple independently deployable services, which is why affected-package detection (Nx, Turborepo) and path-based workflow triggers become standard practice.
Yes, treated as a gate alongside functional tests - dependency audits and image scanning catch supply-chain risk that purely functional tests were never designed to catch.
No - it only proves what its gates were built to check. Code review, production monitoring, and judgment about the specific change's risk still matter, especially for anything a pipeline's gates weren't designed to catch.
It's a deliberate trade-off between speed and control - an approval step lets a human apply judgment about release timing or business risk that automated gates aren't positioned to evaluate.
Running the cheapest, quickest checks (lint, typecheck) before expensive ones (full test suites, builds) so a broken change is rejected in seconds rather than after several minutes of unnecessary work.
An extension of the promotion model where "deploy to production" itself becomes gated and incremental - a new release reaches a small slice of traffic first, and only rolls out further if it looks healthy, so a bad release affects far fewer users before it's caught.
Stack versions: This page is conceptual and not tied to a specific stack version.
Reviewed by Chris St. John·Last updated Jul 15, 2026