DevOpsBeginner

CI/CD Pipelines Explained: From Commit to Production

What continuous integration and continuous deployment actually mean, how a typical pipeline is structured, and where teams commonly get the split wrong.

DevFieldGuideJuly 29, 2026 (updated July 31, 2026)6 min read
Share:

CI and CD get said together so often they're treated as one concept — they're two distinct practices that happen to fit together well.

Continuous Integration: catching problems early

CI means every code change is automatically built and tested as soon as it's pushed — not batched up and tested right before a release. The core value: a bug is caught within minutes of being introduced, when the change causing it is still fresh in the author's mind, rather than weeks later during a pre-release testing pass when nobody remembers which of fifty commits caused it.

A typical CI pipeline, triggered on every push:

yaml
# .github/workflows/ci.yml
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - run: npm install
      - run: npm run lint
      - run: npm run type-check
      - run: npm test
      - run: npm run build

Continuous Delivery vs. Continuous Deployment

This is the split that gets blurred:

  • Continuous Delivery — every change that passes CI is ready to deploy, packaged and verified, but a human still decides when to actually release it (often with one click).
  • Continuous Deployment — every change that passes CI deploys automatically, with no manual approval step. The pipeline itself is the release mechanism.

Continuous Deployment is the more aggressive of the two and requires real confidence in your test suite — without strong automated coverage, shipping every merged commit straight to production automatically just means bugs reach production faster too.

A typical full pipeline

Push codeTriggers the pipeline
Lint + type-checkFails fast, cheapest checks first
Run testsThe trust-bearing gate
Build artifactOnly after tests pass
Deploy to stagingReal environment, not prod
Smoke test stagingCatches env-specific issues
Deploy to productionManual approval, or automatic

Any failed step notifies and stops the pipeline before the next one runs — a failure at "lint + type-check" never reaches "deploy to production."

Where teams commonly get this wrong

Treating "tests pass" as the only gate. A test suite with poor coverage passing doesn't mean the code is safe to ship — it means the parts that are tested didn't break. Pipelines are only as trustworthy as the test suite backing them.

No staging environment, or a staging environment that's drifted from production. Deploying straight from CI to production skips the chance to catch environment-specific issues (config differences, infrastructure quirks) that unit tests can't catch. Even a lightweight staging step catches real problems before real users do.

Slow pipelines that get bypassed. A CI run that takes 25 minutes gets skipped ("I'll just merge, it's a small change") far more than one that takes 3. Parallelizing test suites and caching dependencies aggressively isn't a nice-to-have — a slow pipeline is a pipeline people route around.

Feature flags: decoupling deploy from release

Continuous Deployment's biggest objection — "we can't ship every merge straight to users" — often isn't actually about the pipeline, it's about conflating deploying code with releasing a feature. Feature flags separate the two: code merges and deploys continuously, but a new feature stays behind a flag that's off for real users until it's deliberately turned on.

ts
if (featureFlags.isEnabled("new-checkout-flow", { userId })) {
  return <NewCheckoutFlow />;
}
return <LegacyCheckoutFlow />;

This means the CI/CD pipeline can stay simple and fast (every merge ships) without every merge being feature-complete or user-visible — a half-built feature can live in production, disabled, for days or weeks while it's finished, entirely decoupled from deploy cadence. It also turns a risky release into a reversible configuration change: if a newly-enabled feature causes problems, flipping the flag off is instant, no rollback deploy required.

Rollback strategy: the piece people forget to plan for

A pipeline that deploys automatically needs an equally automatic answer to "what happens when the new version is broken." Two common patterns:

  • Blue-green deployment — two full environments exist; traffic switches from the old ("blue") to the new ("green") only after the new one passes health checks, and switching back is just re-pointing traffic, not a new deploy.
  • Rolling deployment with automated rollback — new instances replace old ones gradually, and a spike in error rate or failed health checks during the rollout automatically halts and reverts to the previous version, rather than requiring a human to notice and intervene.

Without one of these in place, a bad deploy on a real Continuous Deployment setup means broken production until someone notices and manually reverts — the exact opposite of the fast-feedback goal CI/CD exists for in the first place.

Pipeline speed: caching and parallelization

The two highest-leverage changes for a pipeline that's slow enough to be getting bypassed, before reaching for more exotic fixes:

yaml
- uses: actions/setup-node@v6
  with:
    node-version: "22"
    cache: "npm"   # caches node_modules based on the lockfile hash
yaml
strategy:
  matrix:
    shard: [1, 2, 3, 4]
steps:
  - run: npm test -- --shard=${{ matrix.shard }}/4

Dependency caching (keyed on the lockfile so it invalidates only when dependencies actually change) typically removes the single largest fixed cost from every run. Sharding the test suite across parallel jobs then cuts wall-clock time roughly in proportion to the shard count, for test suites that are otherwise the slowest single step in the pipeline.

Where the pipeline connects to everything else

A CI/CD pipeline rarely stands alone — the "build artifact" step usually applies infrastructure changes defined in code (see Infrastructure as Code: Why Terraform Won), and the fast local feedback that keeps commits small and reviewable starts even earlier, at Git hooks running before code is ever pushed.

The actual goal

The point of CI/CD isn't automation for its own sake — it's shrinking the time between "a change is made" and "we know whether it's safe," and then shrinking the time between "it's safe" and "users have it." Every piece of the pipeline should be justified by one of those two goals, not added because it seemed like standard practice.

Advertisement

Frequently Asked Questions

Advertisement
DevFieldGuide
DevFieldGuide

Editorial Team

Practical tutorials and developer tools, written and maintained by the DevFieldGuide team.

Enjoyed this article?

Get the next one straight to your inbox, along with the best of what we publish each week.

Related Articles

More in DevOps

View all