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:
# .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 buildContinuous 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
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.
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:
- uses: actions/setup-node@v6
with:
node-version: "22"
cache: "npm" # caches node_modules based on the lockfile hashstrategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- run: npm test -- --shard=${{ matrix.shard }}/4Dependency 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.
Related reading
- Infrastructure as Code: Why Terraform Won — shares tags: devops, cloud (same category).
- Understanding Cloud Cost Optimization Basics — shares tags: cloud, devops.
- Git Hooks Explained: Automate Your Workflow — shares tags: git, devops.
- Kubernetes ConfigMaps and Secrets: A Practical Guide — shares tags: devops, cloud.
- What Is GitOps? Principles, Workflow, and Why Teams Adopt It — shares tags: devops, git.
- GitHub Actions Reusable Workflows: Build CI/CD Templates Once, Use Everywhere — shares tags: devops, git.