Skip to content

Git, Linux & DevOps

CI/CD Pipelines

Continuous integration (CI) is the practice of automatically building and testing code every time it changes, usually on every push or pull request, so problems are caught within minutes rather than discovered days later. Continuous delivery/deployment (CD) extends that automation to actually releasing the change - to a staging environment, or straight to production - once it passes. Together, a CI/CD pipeline is the sequence of automated steps (build, test, package, deploy) that turns a commit into a running change with minimal manual intervention.

Why it matters

It catches problems close to when they're introduced
A test failing on the commit that broke it, minutes later, is far cheaper to fix than the same failure discovered a week later mixed in with ten other changes.
It removes manual, error-prone release steps
A person running a fifteen-step deployment checklist will eventually skip a step; a pipeline runs the same steps, in the same order, every time.
It makes small, frequent releases practical
When deploying is a scripted, tested, low-effort action rather than an event, teams can ship small changes often instead of batching risk into rare, large releases.
It's where automated checks actually get enforced
A test or a security scan that only runs when someone remembers to run it locally gets skipped under deadline pressure; a check wired into the pipeline blocks the merge until it passes.

What actually runs in a pipeline

A pipeline is usually defined as a file checked into the repository itself, describing a series of stages - build, lint, test, package, deploy - and what triggers them: a push, a pull request, a schedule, or a tagged release. Keeping the definition in the repository means it's versioned, reviewed and changes alongside the code it builds, rather than living as undocumented configuration in a separate tool.

Output
name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm test

Continuous integration vs continuous delivery vs continuous deployment

Continuous integration means every change is automatically built and tested. Continuous delivery goes further and automatically packages and stages a change so it's ready to release, but a person still decides when to actually push it to production. Continuous deployment removes that manual gate entirely and releases straight to production once the pipeline passes. All three get shortened to 'CI/CD', which is why the distinction between delivery and deployment is worth being explicit about.

Environments and promotion

Most pipelines move a change through a sequence of environments - typically development, staging, then production - with the same built artifact promoted forward rather than rebuilt separately for each one, so what's tested is exactly what ships. Feature flags and a fast rollback path act as a safety net for the cases automated tests don't catch, since no amount of automated testing removes all risk from a release.

Mistakes people make here

Treating a green pipeline as proof the software works
a pipeline only checks what it's told to check; a passing build with no tests, or tests that don't cover the changed code, proves nothing about correctness.
Letting the pipeline become the only place tests are run
if developers never run tests locally and only find out a change is broken after pushing, the feedback loop is far slower than it needs to be; CI should be a backstop, not the first check.
Deploying straight to production with no staging step or rollback plan
automation makes it easy to deploy a bad change just as fast as a good one; a staged rollout, health check, or quick rollback path matters more, not less, once deploys are automated.
Storing credentials as plain pipeline variables instead of secrets
many CI systems print full command output to logs; a credential passed as a plain environment variable can end up visible in a log accessible to a wider group than should have access to it.
Ignoring flaky tests instead of fixing them
once a team gets used to re-running a pipeline because 'that test is just flaky', a real failure eventually gets waved through the same way.

Strengths and trade-offs

Where it is strong

  • Turns release steps into code that's reviewed and versioned like anything else, instead of undocumented tribal knowledge.
  • Runs the exact same checks on every change, with no dependence on who remembers to run what.
  • Shortens the time between introducing a bug and discovering it, which is usually the single biggest lever on how expensive a bug is to fix.
  • Makes frequent, small deployments practical, which in turn makes each individual deployment lower-risk.

The trade-offs

  • Pipelines themselves need maintenance - flaky steps, slow builds and outdated actions or plugins are an ongoing cost, not a one-time setup.
  • A slow pipeline becomes a bottleneck the whole team feels on every change.
  • Full automation to production removes a human checkpoint; that's a deliberate trade for speed, and it requires good tests, monitoring and rollback tooling to be safe.
  • Environment parity is harder to get right than it sounds, and a pipeline that passes in staging can still fail in production if the two genuinely differ.

Who needs this

Any developer working on a team with a shared codebase benefits from understanding how the pipeline that gates their merges works, even without owning it. Setting up and maintaining pipelines is more specifically a DevOps or platform-engineering responsibility, but knowing how to read a failed pipeline run is a baseline skill.

Questions about ci/cd pipelines

What's the difference between continuous delivery and continuous deployment?
Continuous delivery means every change that passes the pipeline is ready to release, packaged and staged, but a person still decides when to actually push the button for production. Continuous deployment goes one step further and releases to production automatically with no manual gate. Both are commonly shortened to 'CD', which is why the distinction is worth being explicit about.
Do I need a separate tool from where my code is hosted?
Not necessarily. GitHub Actions, GitLab CI and Bitbucket Pipelines all run pipelines defined in the same repository as the code, triggered by the same platform that hosts it. Standalone tools like Jenkins also exist and are common, especially in older or more complex setups.
What should be in the pipeline versus run only locally?
Anything that gates whether a change is safe to merge or release - tests, linting, security scans, builds - belongs in the pipeline, because that's the only place guaranteed to run for every change. Slower or more exploratory checks can stay local, or run on a schedule rather than every commit, to keep the pipeline fast.
Why does my pipeline pass locally but fail in CI?
Usually an environment difference: a different OS or dependency version, a missing environment variable, or a test that relies on file paths, timing, or state left over from a previous run. Reproducing the CI environment locally, often with the same container image the pipeline uses, is the standard way to track it down.

The primary source

Related concepts

← All concept guides