Skip to content

Home / Glossary / CI/CD (Continuous Integration and Delivery)

Tech & IT

CI/CD (Continuous Integration and Delivery)

By Sitraka Forler · Lecturer, Durham Business SchoolUpdated 3 October 2026 About this site

An automated pipeline that tests every change and moves passing code towards production.

Also known as: continuous deployment.

Continuous integration (CI) means merging small changes often, with every push built and tested automatically so breakage shows up in minutes. CD means continuous delivery, where passing code is always releasable but a person approves each release, or continuous deployment, where every change that passes goes live automatically. A green pipeline only proves what the tests check, so weak tests let bugs through.

The intuition

Before continuous integration, teams worked for weeks on separate branches and then merged everything at once, which is how the phrase integration hell was earned: hundreds of conflicting changes, a build nobody could get working, and no way to tell which change broke what. Continuous integration inverts that. Every push triggers a server to check out your code on a clean machine, install the dependencies from scratch, and run the whole test suite, so the question "does this still work with everyone else's code?" is answered in minutes rather than at the end of the quarter. The clean machine matters as much as the tests: it is the only honest answer to "it works on my machine", because the runner has none of your local files, none of your environment variables and none of the packages you installed months ago and forgot about.

The pipeline itself is just a list of stages written into a file in the repository, such as .github/workflows/ci.yml, saying which events trigger a run and which commands to execute in order. A typical run installs dependencies, lints the code for style, checks the types, runs the tests, builds the artefact and then deploys it. Each command is judged by its exit code: zero means success and the run moves on, anything else stops the pipeline and paints the run red. The three letters split there. CI covers everything up to and including the tests and the build: the code is proved to integrate. Continuous delivery means every green build produces a release-ready artefact that a person can ship with one click. Continuous deployment removes the person: the same green run keeps going and puts the change into production by itself.

At the edges the promise gets thinner. Green means the checks you wrote passed and nothing more, so a suite that never exercises the pricing code says nothing at all about the pricing code. Flaky tests, the ones that fail now and then for reasons unrelated to your change, are worse than no tests, because a team that learns to click rerun stops reading red at all. Speed is the other constraint: a pipeline that takes forty minutes gets worked around, so teams split it into a fast set that runs on every push and a slow set that runs nightly. What practitioners actually do is make the pipeline binding. Branch protection marks the run as a required check, so a red pull request cannot be merged, and the convention is to stop and fix rather than pile more commits on a broken main branch.

Example

# .github/workflows/ci.yml
on: [push]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - run: npm ci && npm test

One pull request through a pipeline, red then green

  1. Start the change: git switch -c refactor/sharpe-periods, edit src/metrics.py so sharpe() takes a periods argument, then git commit -am "refactor: let sharpe() take a periods argument" and git push -u origin refactor/sharpe-periods. The push on its own runs nothing, because this workflow listens for pull_request rather than for push. Open the pull request with gh pr create --base main: that is the event that wakes the pipeline, and every later push to the same branch wakes it again.
  2. The workflow file decides what runs. .github/workflows/ci.yml starts with on: pull_request, then jobs: check: runs-on: ubuntu-latest, then the steps: uses: actions/checkout@v7 (pinned to a major version), a setup-python step pinned the same way with python-version: "3.13", and four run lines in order: pip install -r requirements.txt, ruff check ., mypy src and pytest -q.
  3. The first three stages pass on a machine that is not yours. GitHub hands the job a fresh Ubuntu runner holding none of your files, checks out your branch merged with main, and pip install -r requirements.txt resolves the pinned versions and exits 0 after 55 seconds. ruff check . prints All checks passed! and exits 0 after 6 seconds. mypy src prints Success: no issues found in 3 source files and exits 0 after 12 seconds.
  4. The test stage catches the bug. pytest -q ends with 1 failed, 42 passed, that is 42 + 1 = 43 tests, in 48 seconds, and exits non-zero, so the job stops on that step and the run is red. The failing test is tests/test_metrics.py::test_sharpe_annualises_monthly_returns and the assertion line reads assert 0.25 == 0.866 ± 1.0e-04.
  5. Read the numbers rather than the red cross. The fixture feeds monthly excess returns with mean 0.005 and standard deviation 0.02, so the monthly Sharpe ratio is 0.005 / 0.02 = 0.25. Annualised it should be 0.25 × √12 = 0.25 × 3.4641 = 0.8660, which is the 0.866 the test expects. Your refactor dropped the * math.sqrt(periods) multiplier, so the function returned the monthly 0.25 instead.
  6. The red run blocks the merge. main is protected with check listed as a required status check, so the pull request page shows Some checks were not successful and the merge button stays disabled. Nobody else pulls the bug, because it never reaches main: this is the whole return on the pipeline.
  7. Fix and push again. Restore the multiplier, return excess.mean() / excess.std(ddof=1) * math.sqrt(periods), then commit and push. The same workflow runs on the new commit and pytest -q now reports 43 passed. The same four commands cost 55 + 6 + 12 + 48 = 121 seconds, that is 2 minutes and 1 second of feedback, with the checkout and Python setup steps adding a few seconds ahead of them, and the pull request becomes mergeable.
  8. After the merge, the CD half decides what happens next. Under continuous delivery the run on main builds the artefact and stops at an approval gate on the production environment, so a person clicks Approve when the team is ready. Under continuous deployment there is no gate: the same green run deploys, and the one-line fix is live a couple of minutes after the merge.

A pipeline is nothing more than your own commands run by a server on a clean machine after every push, and its value is the bug it stops: a dropped √12 caught in 121 seconds on a pull request instead of in a client report a month later.

Common pitfalls

  • Reading green as "the code is correct". A pipeline only runs the checks you wrote, so untested code stays untested whatever colour the badge is. Ask what the suite actually exercises before you trust a passing run.
  • Tolerating flaky tests. A test that fails at random teaches the team to hit rerun until the job is green, which destroys the one thing a pipeline is for: red meaning something. Quarantine the flaky test and fix it instead of retrying the whole job.
  • Running the checks only after the merge. If the pipeline triggers on main rather than on the pull request, the first people to learn about a breakage are everybody else. Trigger on pull_request and make the job a required status check so a red run blocks the merge.
  • Letting the runner drift from your machine. Installing from an unpinned dependency list, or relying on a file you never committed, produces the classic green locally and red in CI, and sometimes the more dangerous reverse. Install from a lockfile and treat the runner's clean checkout as the truth.
  • Printing secrets into the build log. GitHub Actions redacts the values it knows about, so a bare echo $API_KEY usually prints ***, but that masking is best effort: it does not cover a value you never registered as a secret, and it is defeated as soon as the value is transformed, base64 encoded or split across lines. A debugging env dump is exactly how one slips into a log that anyone with read access to the repository can open, long after the run. Read secrets from the CI secret store, add a mask for anything sensitive that is not one, and never echo them.

Frequently asked questions

What is CI/CD in simple terms?

CI/CD is the practice of letting a server check every change automatically and carry the good ones towards production. CI, continuous integration, means each push is built and tested on a clean machine within minutes, so breakage surfaces immediately. CD means the passing code is either always ready to release, which is continuous delivery, or released with no human step at all, which is continuous deployment.

What is a CI/CD pipeline?

A pipeline is the ordered list of commands a CI server runs for you, defined in a file inside your repository such as .github/workflows/ci.yml. A common order is install dependencies, lint, check types, run the tests, build the artefact and deploy. Each stage must exit with code 0 for the next one to start, so by default the first non-zero exit stops the run and marks it red.

What is the difference between continuous delivery and continuous deployment?

Both begin from a pipeline that is green. Continuous delivery stops at a person: every passing build is packaged and releasable, but somebody approves the release. Continuous deployment removes that gate, so every change that passes goes to production on its own. Delivery is about being able to release at any moment, deployment is about actually doing it every time.

What does it mean when a build is red or broken?

A red build means one stage of the pipeline exited with a non-zero code: a failing test, a lint error, a type error or an install that could not resolve. Nothing after that stage ran. The convention is to treat it as the team's first priority: open the log, find the first failing stage, then fix or revert, because anything merged on top of a broken main branch inherits the problem.

Do I need CI/CD on a solo or small project?

A minimal pipeline pays for itself even when you work alone, because it is the only thing that proves your project runs somewhere other than your laptop: fresh checkout, fresh install, tests run. Ten lines of configuration that call pytest on every push will catch the dependency you never added to requirements.txt and the file you forgot to commit. Deployment can come later.

What tools are used for CI/CD?

GitHub Actions, GitLab CI/CD, Jenkins, CircleCI and Azure Pipelines are the ones you are most likely to meet, and hosting platforms such as Vercel run a build and deploy step of their own on every push. They differ mainly in configuration syntax and pricing. The concepts, triggers, jobs, stages, exit codes, artefacts and secrets, carry across all of them.

Test yourself

Practise CI/CD (Continuous Integration and Delivery) hands-on - free, in your browser

Related terms