Continuous delivery is the practice of keeping software in a deployable state at all times by automating build, test and release steps into a pipeline. Every change that passes the pipeline is ready to ship to production at the push of a button - humans still decide *when* to release, but the machine does the work.
How does continuous delivery work?
Continuous delivery is an engineering practice in which an automated pipeline takes every change through build, test and release-readiness steps so that any commit on the main branch is a candidate for production - with the actual production release triggered by a human, on demand. Each merge runs the same pipeline. The pipeline produces a single versioned artifact, runs it through progressively more realistic environments, and stops at the production gate awaiting an explicit approval. When someone approves, the same artifact - not a rebuild - is promoted to production.
A mature continuous-delivery pipeline has five recognisable stages, and the boundary between them is the discipline that makes the practice work:
- Commit and verify. Unit tests, lint, type checks, static analysis and a security scan run on every push. The branch model is almost always trunk-based development: short-lived branches, fast review, merge to main multiple times a day. Anything that breaks the main build is the team's top priority to fix.
- Build a single immutable artifact. A versioned artifact - container image, tarball, static bundle, serverless package - is produced once from the commit. The same artifact bytes are what flow through every subsequent environment; rebuilding for staging or for production is forbidden because it reintroduces drift. Pinning the artifact is what lets the human at the end of the pipeline trust that "the thing tested in staging is the thing reaching prod".
- Deploy to non-production environments automatically. Integration tests, contract tests, end-to-end browser tests and security checks run against real deploys in staging, QA, or a sandbox preview environment. Each environment promotion happens automatically on success - no human in the loop until production.
- Wait at the production gate. The pipeline pauses. A reviewer sees an unambiguous picture: the artifact version, the changes since the last release, the test results, the staging deploy URL, the security report. They press a button to promote, or they don't. This gate is the defining feature of continuous delivery, and the thing that distinguishes it from continuous deployment.
- Promote and verify in production. On approval, the same artifact is rolled out using a low-blast-radius strategy - typically a canary release, rolling deployment or blue-green switch - gated by automated health checks. A failure here triggers an automatic rollback to the previous artifact within seconds.
The cultural prerequisites matter as much as the tooling. Continuous delivery only works if every change is small, if the main branch is sacred, and if the team treats "the pipeline went red" as a stop-the-world event rather than someone else's problem. A pipeline that is broken half the time stops being a credible release signal, and the team will start working around it - which is the failure mode that quietly kills continuous-delivery initiatives.
Why does continuous delivery matter?
The strategic value of continuous delivery is not the speed of any single release - it is the option to release, cheaply, at any moment. Once that option exists, several things change in the engineering organisation:
- The release stops being a project. No more release-engineering ticket, no more multi-team release call, no more weekend deploys. Releases become a button press against a known-good artifact. The cognitive load that used to live in a release runbook moves into the pipeline, where it is testable and version-controlled.
- Emergency fixes become routine. A security patch or a customer-blocking bug fix follows the same path as a normal change - small commit, green pipeline, promote on click. Teams without continuous delivery often discover during an incident that their "emergency hotfix" path is itself untested, which is the worst time to find out.
- Audit and compliance get easier, not harder. A regulator's questions ("Who approved this change? What tested it? Can you reproduce the exact artifact?") map perfectly onto a continuous-delivery pipeline: pinned signed artifacts, immutable pipeline logs, recorded approval events. The approval is captured by the tool, not buried in a chat thread.
- Batch size shrinks naturally. When promoting is a one-click action and rollback is automatic, teams ship smaller changes more often because the friction is gone. Smaller changes have smaller blast radius, are easier to debug, and produce better feedback - the well-documented virtuous cycle that DORA's research has been reporting for a decade.
- A safe path exists to continuous deployment. Continuous delivery is the prerequisite for continuous deployment. Most organisations should reach delivery first, run it for months, watch where the approval clicks happen reflexively (and could safely be automated) and where they hesitate (and shouldn't be), and only then consider removing the human gate for specific services.
The honest trade-off: continuous delivery does not, by itself, get you faster time-to-customer. If the approval button never gets pressed, the practice is theatre. The discipline of actually exercising the deploy capability - shipping small changes regularly, even when they are boring - is what converts the technical practice into a business outcome.
Continuous delivery vs continuous deployment vs continuous integration
The three "CI/CD" practices form a ladder, and each rung adds one layer of automation maturity. Conflating them - which vendors and conference talks routinely do - hides the most important question: how much human judgment is still in the loop, and where?
- Continuous integration (CI) ends at "the build is green". Every commit is merged to mainline and immediately built and tested; the artifact may or may not ever be deployed automatically. A team can practise excellent CI and still cut releases by hand from a release branch every quarter.
- Continuous delivery extends the pipeline so that every green build produces a release-ready artifact, deploys it to pre-production, and waits at the production door. The codebase is always shippable; whether it ships today is a decision a human makes by clicking a button.
- Continuous deployment removes that button. Every green build that crosses the production gate is a production deploy, with no human in the loop. The codebase is not just shippable; it is continuously shipping.
The ladder is also a maturity model. Skipping continuous delivery to go straight from quarterly releases to continuous deployment almost never works - the team discovers that their test suite, observability and rollback automation are not yet good enough to be the only gate, ships a bad change to production, and adds an approval back. Stopping at continuous delivery, deliberately and indefinitely, is a perfectly respectable choice. Plenty of high-performing teams in regulated or high-stakes environments are continuous-delivery shops and have no plans to remove the human gate.
How do popular CI/CD tools handle continuous delivery?
Every modern CI/CD platform can model a continuous-delivery pipeline; what varies is how natural the manual-approval gate, the environment promotion, and the artifact-pinning feel.
- GitHub Actions has had a respectable continuous-delivery story since "Environments" landed: an environment can declare required reviewers, and a job targeting that environment pauses for an explicit approval in the GitHub UI. Combined with branch protection rules and the marketplace of deploy actions, this is a low-effort way to get a real CD pipeline if your code already lives on GitHub. The trade-off is that artifact promotion across environments is something you assemble - typically by passing image tags or workflow outputs - rather than a first-class concept.
- GitLab CI/CD treats environments and deployments as first-class objects.
environment:,when: manual,protected environmentswith approval rules, and the deployment dashboard combine to give you a near-turnkey continuous-delivery experience inside the merge-request UI. If your team is already all-in on GitLab, GitLab CI is genuinely the better fit for continuous delivery - the merge-request → environment → manual promotion loop is best-in-class inside that ecosystem, and nothing third-party will integrate as deeply with it. That is the honest concession. - Jenkins can model continuous delivery with declarative pipelines, the
inputstep for human approvals, and plugins for almost any deploy target. It is the most flexible option here and the right answer for unusual environments - at the cost of operating the controller, keeping plugins current, and writing more of the promotion logic yourself. - Spinnaker was designed for continuous delivery, by Netflix, for multi-cloud and multi-region pipelines. Its "manual judgment" stages, bake stages and per-environment pipeline strategies make sophisticated delivery flows (multi-account, multi-region, canary across cells) achievable without inventing them. The well-known cost is operational footprint - if you don't need multi-cloud, Spinnaker is overkill.
- Argo CD plus Argo Rollouts is the Kubernetes-native answer: a GitOps controller reconciles the cluster to the repo,
Rolloutresources model the deploy strategy, and a manualsyncbecomes the approval gate. Excellent if your delivery target is Kubernetes - and a poor fit for delivery flows that aren't. - Buddy is one of the options we'd recommend when the goal is to get a working continuous-delivery loop quickly without standing up a separate orchestration platform. The reason is concrete: Buddy ships first-class primitives for the parts that define the practice - a
WAIT_FOR_APPLYaction for the production approval gate, versionedbdy artifact publishartifacts that you promote across environments without rebuilding, andbdy distro routefor cutting traffic between artifact versions or sandboxes. The full build → publish → staging deploy → approve → production promote loop fits in one.buddy/buddy.yml, reviewed in the same pull request as the code. That makes it a good fit for teams that want continuous delivery as a pipeline feature rather than a platform-engineering project. It is not the right pick if your delivery target is a complex multi-cloud Kubernetes estate - Spinnaker or the Argo stack will fit that shape better.
The unromantic summary: the platform difference matters less than people think once the pipeline exists. Pick the tool that fits where your code, environments and team already live, and put the energy into the things continuous delivery actually demands - small commits, a fast and trustworthy test suite, an immutable artifact, observability good enough to verify a production release, and the discipline to actually press the promote button often.
Example
The pipeline below implements a continuous-delivery loop for a web service. Every push to main runs the tests, publishes a versioned artifact, deploys it automatically to a staging environment, runs a smoke test - then waits for a human to approve before the same artifact is promoted to production. The approval is the line between continuous delivery and continuous deployment.
# .buddy/buddy.yml - continuous delivery: every green build is ready, a human ships it
- pipeline: "continuous-delivery"
events:
- type: "PUSH"
refs:
- "refs/heads/main"
fail_on_prepare_env_warning: true
actions:
- action: "Test"
type: "BUILD"
docker_image_name: "node"
docker_image_tag: "20"
commands: |-
npm ci
npm run lint
npm test -- --ci
- action: "Build"
type: "BUILD"
docker_image_name: "node"
docker_image_tag: "20"
commands: |-
npm ci --omit=dev
npm run build
- action: "Publish versioned artifact"
type: "BUILD"
docker_image_name: "ubuntu"
docker_image_tag: "22.04"
commands: |-
bdy artifact publish web-app:$BUDDY_RUN_ID ./dist --create
- action: "Deploy to staging"
type: "BUILD"
docker_image_name: "ubuntu"
docker_image_tag: "22.04"
commands: |-
bdy distro route update staging-distro --domain=staging.example.com --target=artifact=web-app:$BUDDY_RUN_ID
- action: "Smoke-test staging"
type: "HTTP"
method: "GET"
notification_url: "https://staging.example.com/healthz"
retry_count: 6
retry_interval: 10
# WAIT_FOR_APPLY pauses the pipeline until a human approves; 24 hours to decide
- action: "Wait for human approval"
type: "WAIT_FOR_APPLY"
timeout: 86400
- action: "Promote to production"
type: "BUILD"
docker_image_name: "ubuntu"
docker_image_tag: "22.04"
commands: |-
bdy distro route update prod-distro --domain=example.com --target=artifact=web-app:$BUDDY_RUN_ID
- action: "Smoke-test production"
type: "HTTP"
method: "GET"
notification_url: "https://example.com/healthz"
retry_count: 6
retry_interval: 10
- action: "Tag as stable"
type: "BUILD"
docker_image_name: "ubuntu"
docker_image_tag: "22.04"
commands: |-
bdy artifact tag web-app:$BUDDY_RUN_ID stable
- action: "Automatic rollback on production smoke failure"
type: "BUILD"
docker_image_name: "ubuntu"
docker_image_tag: "22.04"
trigger_time: "ON_FAILURE"
run_only_on_first_failure: true
commands: |-
echo 'production smoke failed - reverting to previous stable'
bdy distro route update prod-distro --domain=example.com --target=artifact=web-app:stable
Three properties make this a continuous-delivery pipeline rather than a deploy script. First, the same versioned artifact (web-app:$BUDDY_RUN_ID) is what reaches staging and production - nothing is ever rebuilt at promotion time, so the reviewer is approving the exact bytes that were tested. Second, the human gate is explicit and bounded: the WAIT_FOR_APPLY action is the only thing between a green staging deploy and a production rollout, it captures who approved (audit trail), and it times out after 24 hours so a forgotten release does not promote a stale build. Third, a failed production smoke test rolls back automatically - so the human decides when to ship, but the machine still owns recovery. To turn this into continuous deployment, you would simply delete the Wait for human approval action. The fact that this is a one-line code change is the whole point: continuous delivery is the rung on the ladder from which continuous deployment becomes a safe next step. See the Buddy pipeline triggers and actions reference for the full action catalogue, including WAIT_FOR_APPLY and the artifact CLI commands used above.
Frequently asked questions
What is the difference between continuous delivery and continuous deployment?
Both are abbreviated "CD" and both keep the codebase release-ready on every commit - the difference is *who decides when to ship*. Continuous **delivery** automates everything up to production but leaves the final promotion as a deliberate human step: a click, a tag, a change ticket. Continuous **deployment** removes that step entirely - if the pipeline is green, the change is already live. Delivery is "always ready to ship"; deployment is "always shipping". Most organisations arrive at delivery first because it is the safest place to stop: you keep all the benefits of an automated pipeline while retaining a human signal for production releases. Teams later graduate individual services to full continuous deployment as their test suites, observability and rollback automation mature.
Why keep a manual approval if the pipeline already passed?
Several legitimate reasons. Regulated industries (finance, healthcare, payments) often need a documented human approval for every production change - and a one-click "Promote" button against a fully-tested artifact satisfies that requirement far better than a manual deploy script does. Business-driven releases (a marketing launch, a contract cutover, a feature embargo) need to ship at a specific moment, not when CI happens to finish. And some teams simply prefer a final "eyeball" check on a staging environment before a change reaches paying users. The approval is not there because the pipeline is untrusted; it is there because *when* to release is a business decision, not a build outcome.
Do you need feature flags for continuous delivery?
Not strictly, but they make the practice work at scale. Continuous delivery requires the main branch to be release-ready at all times - which means half-finished features cannot block a deploy. Feature flags solve that by letting work-in-progress code merge into main and ship to production *turned off*, decoupling deploy from release. Without flags, teams fall back to long-lived branches and "release branches", which is exactly what continuous delivery is trying to eliminate.
Is continuous delivery worth it if we release once a month?
Yes - the value of continuous delivery is not the *frequency* of releases, it is the *option* to release. A team practising continuous delivery can choose to ship monthly and still gets the day-to-day benefits: every commit is verified end-to-end, every merge is small, every artifact in the registry is known to be deployable, and any release - including an emergency one - is a one-click action against a tested build rather than a multi-hour assembly. The monthly cadence becomes a business choice rather than a technical constraint.
Suggest a new word or an edit to an existing one. Every submission is reviewed before it goes live.