Continuous integration validates every change, continuous delivery keeps the code deployable at any time, continuous deployment puts it in production with no human intervention. The last two share the "CD" acronym, hence the confusion. Choosing the level is not a matter of vocabulary: it shapes the team's culture, its testing requirements and the risk it accepts. This lesson defines the three practices, helps you choose your level and shows a complete chain that links them.
What you will learn
- Tell continuous integration, continuous delivery and continuous deployment apart
- Apply the four rules that make CI effective
- Justify a human approval before production
- Check the prerequisites of continuous deployment before adopting it
- Read a complete chain and locate its approval gate
- Recognise the trap of premature "full CD"
The three levels of automation
Each level includes the previous one: no continuous delivery without continuous integration, no continuous deployment without continuous delivery. It is a progression, not a choice between three equivalent options. The diagram shows where each one stops: CI stops at validation, delivery at a ready artifact, deployment at production.
Continuous integration, the mandatory foundation
Continuous integration (CI) validates every change as soon as it is pushed, before it joins the main branch. Picture five developers, each on their own branch: Alice pushes on Monday, Bob on Tuesday, each piece of code compiles on its author's machine, and on Friday the merge no longer compiles, because the two changes are incompatible without anyone knowing.
With CI, the problem shows up immediately: as soon as Alice pushes, the pipeline checks that her code works with the rest, and she knows within minutes rather than five days. On every commit or pull request, the pipeline compiles, runs the unit and integration tests, static analysis and a scan for vulnerable dependencies.
Why CI is not negotiable
The later a bug is found, the more it costs: at commit time, its author still has the context in mind; in production, you have to find the faulty change, fix it under pressure and redeploy. CI brings detection as close as possible to the writing of the code.
The four rules of effective CI
CI only protects if it is respected. Every commit triggers it, without exception; a failure blocks the merge, so that no broken code reaches the main branch; tests stay fast, ideally under ten minutes, otherwise developers bypass the system; and feedback goes straight to the developer, in their interface or messaging tool.
Continuous delivery: ready to deploy, on decision
Continuous delivery extends CI: the code is not only validated, it is ready to go to production at any time. A human decides when. After CI, the pipeline builds an immutable artifact (image, binary, package), deploys it to staging, runs end-to-end tests on complete journeys, then waits for an approval.
Why keep a human approval?
Human approval is not an admission of weakness: it is rational in four situations. A feature must ship together with a commercial announcement, and automatic deployment does not read the marketing calendar. A regulated sector (health, finance, aerospace) requires every production release to be documented and approved. The team does not yet have enough tests to do without it. Or the deployment affects partners who must be warned.
The key point fits in one sentence: with continuous delivery, the code is always deployable. The question is no longer "can we deploy?" but "do we want to deploy now?". It is this permanent availability that counts, not the absence of a button.
Continuous deployment: no human intervention
Continuous deployment removes the last barrier: every validated change goes automatically to production. A developer merges their pull request at 2:03 pm; at 2:08 pm, the code serves real users, without anyone clicking "deploy". As early as 2011, Amazon announced a production deployment every 11.6 seconds on average.
The prerequisites, and they are strict
With no human in the loop, a bug that passes the tests reaches production. Continuous deployment therefore requires five foundations. Tests covering every critical path. Feature flags, which ship disabled code and let you switch a feature off without redeploying. An automatic rollback when error rates or latency degrade.
It also takes observability that detects a problem within seconds, with alerts, dashboards and centralised logs: you cannot fix what you cannot see. And a blameless culture, where an incident serves to improve the system rather than to point at a culprit, otherwise developers will be afraid to merge. The teams that work this way have invested years in it: it is a destination, not a starting point.
Side-by-side comparison
The table sets the three practices against eight criteria. Read the "production deployment" and "human intervention" rows above all: that is where the difference between the two CDs lies. The other rows show what each level adds in requirements, especially in testing and rollback.
| Aspect | CI | Continuous delivery | Continuous deployment |
|---|---|---|---|
| Trigger | Commit, pull request | Merge into main | Merge into main |
| Artifact produced | Optional | Mandatory | Mandatory |
| Staging deployment | Optional | Mandatory | Mandatory |
| Production deployment | No | Manual | Automatic |
| Rollback | Not applicable | Manual | Automatic |
| Testing requirement | Unit | End-to-end | End-to-end and canary |
| Time to production | Not applicable | Hours to days | Minutes |
| Human intervention | None | Final approval | None |
How do you choose your level of automation?
The right level is the one the team can sustain, and it is climbed in order. Start with CI if it is missing: it is the top priority, with a clear minimum goal, every pull request triggering a pipeline, tests that block the merge on failure and feedback in under ten minutes. The rest can wait until this foundation is in place.
Once CI is stable, move to continuous delivery: immutable artifacts, automatic deployment to staging after each merge, end-to-end tests, human approval before production. It is the sufficient level for most teams, and human approval is often the right answer there, not a failure.
Continuous deployment is considered next, and only if four answers are "yes": are the critical paths covered by automated tests? Is a production problem detected within a few minutes? Is rollback automatic? Does the team handle incidents without looking for a culprit? A single "no", and continuous delivery remains the right choice.
What does a complete chain look like, from CI to production?
A continuous delivery chain links the three levels in a single file: the CI part validates every change, the CD part deploys to staging then to production, and an approval gate separates the last two steps. The file below is written for GitHub Actions: it reads without knowing the syntax, which the GitHub Actions course teaches, and it illustrates what the lessons of the course build step by step.
The CI jobs (lint, test, security) run in parallel, then build only
starts if all three succeed, thanks to the needs key. The CD part follows the
same chaining up to production. The image is built only once, tagged with
the commit SHA, and that is the image the environments receive.
name: CI/CD Pipeline
on: push: branches: [main] pull_request: branches: [main]
# No permission by default: each job only receives what it needspermissions: {}
env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }}
jobs: # ===== CONTINUOUS INTEGRATION =====
lint: runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '20' cache: 'npm' - run: npm ci - run: npm run lint
test: runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '20' cache: 'npm' - run: npm ci - run: npm run test -- --coverage - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
security: runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Run Trivy uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0, pinned by SHA with: scan-type: 'fs' severity: 'CRITICAL,HIGH' - name: Check secrets uses: gitleaks/gitleaks-action@e0c47f4f8be36e29cdc102c57e68cb5cbf0e8d1e # v3.0.0
build: needs: [lint, test, security] runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 - uses: docker/login-action@06fb636fac595d6fb4b28a5dfcb21a6f5091859c # v4.5.0 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: push: ${{ github.event_name != 'pull_request' }} tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max
# ===== CONTINUOUS DELIVERY =====
deploy-staging: if: github.ref == 'refs/heads/main' needs: build runs-on: ubuntu-latest environment: staging steps: - name: Deploy to staging run: | echo "Deploying to staging..." # kubectl, ArgoCD, or another deployment tool
integration-tests: needs: deploy-staging runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Run E2E tests on staging run: npm run test:e2e env: BASE_URL: https://recette.inventaire.lab
deploy-production: needs: integration-tests runs-on: ubuntu-latest environment: name: production url: https://inventaire.lab steps: - name: Deploy to production run: | echo "Deploying to production..." # Deployment with a Blue/Green or Canary strategyTwo security settings frame this file: permissions: {} removes every default
right from the GitHub token, each job receiving only the strict minimum
(packages: write to publish the image), and persist-credentials: false
keeps the token from staying on the runner's disk after the checkout. The
Deploy steps only run an echo: the real deployment depends on the target.
The environment: production key is what makes this file continuous
delivery rather than continuous deployment. On GitHub, it triggers a
configurable protection rule: manual approval, branch restriction or
wait timer. Remove the rule, and the same file goes to production with no
intervention: the level of automation is tuned without duplicating the
pipeline.
The "full CD" trap
Some teams want to "do continuous deployment" to tick a box or because a big company does it, without having the prerequisites. The result is predictable: bugs in production every day, developers who are afraid to merge and therefore merge less often, manual rollbacks in a hurry, then a loss of confidence in the pipeline.
Continuous deployment is not a goal in itself, it is the consequence of an acquired maturity. The right approach is to stay with continuous delivery and strengthen tests, observability and culture. The day human approval no longer adds anything, because the tests catch what it would have seen, moving to continuous deployment becomes natural.
Key points
- CI validates every commit automatically: it is mandatory for every team.
- Continuous delivery keeps the code always deployable, the production release remaining a human decision.
- Continuous deployment releases to production without human intervention, and requires tests, feature flags, automatic rollback, observability and a blameless culture.
- Each level includes the previous ones: there is no shortcut.
- A human approval is often the right answer, not a failure.
- Premature "full CD" creates more problems than it solves.
Knowledge check
Check that the essentials of this lesson are in place. The questions only cover what has just been explained here.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Trunk-Based Development and Git branching models
Continue the course: Testing strategies: the safety net of DevOps
A new full quiz with random questions
Work again only on the questions you missed