What is CI/CD? Pipelines explained with a GitHub Actions example

What is CI/CD? Continuous integration and delivery explained with the stages of a pipeline and a GitHub Actions workflow linted with actionlint 1.7.12.

How a CI/CD pipeline ships code: cover for What is CI/CD? Pipelines explained with a GitHub Actions example

CI/CD stands for continuous integration and continuous delivery or deployment. Atlassian defines continuous integration as automating the integration of code changes from many contributors into one project, with builds and tests run after each merge. Continuous delivery adds automatic deployment to a test or production environment after the build, and continuous deployment releases every change that passes all pipeline stages with no human intervention.

Last updated: 11 November 2026.

What is the difference between CI and CD?

Continuous integration checks every change, continuous delivery keeps every passing change ready to release, and continuous deployment releases it automatically. The letters CD can mean either of the last two, which is the usual source of confusion. The table follows Atlassian's definitions.

PracticeWhat happensWho releases
Continuous integration (CI)Developers merge to the main branch as often as possible, and each change is validated by a build and automated testsNot part of CI
Continuous deliveryAfter the build, every change is deployed automatically to a testing or production environment, so the application can be released at any timeA person decides, for example with the click of a button
Continuous deploymentEvery change that passes all stages of the production pipeline is released to customersNobody: only a failed test stops a release

The DORA research programme (dora.dev) frames the same ideas as capabilities. It describes continuous delivery as the ability to release changes of all kinds on demand quickly, safely and sustainably, and continuous integration as a response to a simple fact: a small change to one file can have unintended side effects on the whole system.

What are the stages of a CI/CD pipeline?

A CI/CD pipeline is the automated sequence of stages that takes a code change from a commit to a release. The stages below combine DORA's description of CI with the stage names that GitLab and Jenkins use in their documentation, and the last column shows where each one appears in the GitHub Actions example further down.

StageWhat it doesIn the GitHub Actions example
TriggerA commit or merge starts the pipelineon: push and pull_request
BuildAn automated script creates a package that can be deployed to any environment, numbered and repeatablepip install -r requirements.txt (the sample has no packaging step)
TestAutomated tests run on each change and give feedback within a few minutespython -m pytest -q
DeployAutomation releases the build to a test or production environmentThe deploy job
FeedbackThe status of the build is visible to the teamThe workflow run's pass or fail status

GitLab's documentation names build, test and deploy as typical stages, and the Jenkins handbook shows a three-stage pipeline with Build, Test and Deploy stages. Stage names differ by tool, and GitLab's documentation says stages define the order of execution.

How do you write a GitHub Actions workflow?

GitHub Actions is GitHub's continuous integration and continuous delivery platform. A workflow is a YAML file in the .github/workflows directory of a repository. It runs when an event occurs, such as a push or a pull request, and contains one or more jobs. Each job runs on its own runner, which GitHub describes as a fresh virtual machine, and its steps either run a script or use a reusable action.

The sample project is hypothetical and tiny: a function that adds GST to a price, and two tests.

# pricing.py
def price_with_gst(price: float, rate: float = 0.18) -> float:
    """Return the price including GST at the given rate."""
    return round(price * (1 + rate), 2)

# test_pricing.py
from pricing import price_with_gst

def test_default_rate():
    assert price_with_gst(1000) == 1180.0

def test_zero_rate():
    assert price_with_gst(500, 0) == 500.0

The first half of the workflow, saved as .github/workflows/ci.yml, runs the tests on every push to main and on every pull request:

name: CI

on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-python@v7
        with:
          python-version: "3.12"
          cache: pip
      - run: pip install -r requirements.txt
      - run: python -m pytest -q

The second half adds a deploy job in the same file. It waits for the test job, runs only on main and refers to a deployment environment:

  deploy:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v7
      - run: ./deploy.sh
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

The file's requirements.txt contains one line, pytest, and deploy.sh stands for whatever deployment script a real project has. A real script needs a credential, which the env block passes in from a secret named DEPLOY_TOKEN. GitHub's documentation says a secret reaches a step only when it is set as an input or an environment variable, that an expression for a secret that has not been set returns an empty string, and that a job cannot read an environment's secrets until the required approvers approve.

Each key has a documented job. The runs-on value ubuntu-latest selects a standard GitHub-hosted Linux runner, and GitHub's runner reference lists ubuntu-latest alongside ubuntu-24.04, ubuntu-22.04 and ubuntu-26.04. The uses lines call actions: actions/checkout fetches the repository, and actions/setup-python installs Python 3.12 and, with cache: pip, caches pip dependencies.

The needs key makes the deploy job wait for the test job, and GitHub's documentation says that if a needed job fails or is skipped, the jobs that need it are skipped. The if condition on github.ref stops the deploy job from reaching a runner unless the branch is main. The environment key refers to a deployment environment, and the documentation says all deployment protection rules must pass before the job is sent to a runner.

What was tested, and what was not?

The workflow was linted with actionlint 1.7.12, an MIT-licensed static checker for workflow files, and the commands in the test job were run locally. The workflow itself was not run on GitHub, because act, the tool that runs workflows locally, needs Docker and none is installed on the machine used.

With the file above, actionlint printed nothing and exited with code 0. To confirm that it checks real problems, a copy with needs: tset, github.rf in place of github.ref and runs-on: ubuntu-lastest was rejected with exit code 1 and messages such as 'job "deploy" needs job "tset" which does not exist in this workflow' and 'property "rf" is not defined'.

The test job's two commands were run on Python 3.12.13 with pytest 9.2.0, and printed:

..                                                                       [100%]
2 passed in 0.01s

Changing the function to add 0.01 to the rate made the same command report 2 failed, with "assert 1190.0 == 1180.0" and "assert 505.0 == 500.0". In the workflow, a failing test step fails the test job, and because of needs the deploy job never starts. That is the point of a pipeline: a change that fails its tests is stopped before the deploy job. The action versions, actions/checkout v7 and actions/setup-python v7, were the latest major versions on GitHub on 11 November 2026, and major versions move on.

Which CI/CD tools are common?

GitHub Actions, GitLab CI/CD, Jenkins and CircleCI each define a pipeline in a file kept with the code. The table lists the file each tool reads, according to its own documentation.

ToolPipeline fileNotes from the tool's documentation
GitHub ActionsYAML files in .github/workflowsWorkflows run on GitHub-hosted or self-hosted runners
GitLab CI/CD.gitlab-ci.yml in the project rootDefines stages, jobs and scripts; typical stages are build, test and deploy
JenkinsJenkinsfile checked into source controlAn extensible automation server usable as a simple CI server or a continuous delivery hub
CircleCI.circleci/config.ymlA configuration reference documents the keys, such as version

A pipeline can also build a container image and push it to a registry, which Docker for beginners explains, and a platform such as the one in what is Kubernetes can then run that image. Kubernetes itself does not build an application or deploy source code, according to its documentation, so a pipeline does that work.

Is GitHub Actions free?

GitHub Actions is free for standard GitHub-hosted runners in public repositories and for self-hosted runners, according to GitHub's billing documentation. For private repositories, each GitHub account receives storage and a monthly amount of included compute usage that depends on the account's plan, and additional usage is billed to the account. Plans, included amounts and prices change, so the billing page is the place to check current terms.

What are the common CI/CD mistakes?

The common CI/CD mistakes are slow feedback, flaky tests, leaked secrets and overly broad permissions.

Slow pipelines come first. DORA says each commit should trigger tests that give feedback in a few minutes, and that tests must run in a few minutes or less, because developers will not run slow tests often and a failure then hides among many changes. Caching dependencies, as the sample does with cache: pip, is one documented way to cut the wait.

Flaky tests are next. Martin Fowler wrote in 2011 about non-deterministic tests, which sometimes pass and sometimes fail, and said that left uncontrolled they can destroy the value of an automated regression suite. His advice is to quarantine such tests at first and fix them soon, and he lists lack of isolation, asynchronous behaviour, remote services, time and resource leaks as common causes. A pipeline that people re-run until it goes green has stopped being a safety check. The QA and software testing roadmap covers how test suites are built.

Secrets and permissions complete the list. GitHub's secure-use guidance warns that a secret can be transformed in several ways, so automatic redaction in logs cannot be relied on, and that anyone with write access to a repository can read all its secrets. It advises giving the GITHUB_TOKEN the minimum permissions, with read-only repository contents as the default, which is why the sample sets permissions: contents: read.

The same guidance calls a workflow dependency lockfile, which records the exact commit of every action a workflow uses, including transitive dependencies, the strongest protection against a compromised third-party action. For repositories that cannot adopt a lockfile, it says pinning each action to a full-length commit SHA mitigates the risk of a backdoor in the action's repository. The newest checkout action, v7, refuses by default to check out fork pull request code when a workflow runs on pull_request_target or workflow_run, because those triggers run with the base repository's secrets.

How do you know whether a pipeline is working?

DORA measures software delivery with five metrics: change lead time, deployment frequency, failed deployment recovery time, change fail rate and deployment rework rate. Change lead time is the time from a commit in version control to deployment in production, and deployment frequency is how often deployments happen. A pipeline that shortens the first and raises the second without raising the change fail rate is doing its job. Reading these numbers is separate from building the pipeline, and teams that want the full picture can follow DORA's metrics guide.

Where does CI/CD fit in a forward deployed engineer path?

The Forward Deployed Engineer (FDE) Cohort is a Leap by Masai programme for engineers with hands-on technical experience, run as an in-person start in Goa, an online build phase and an in-person hackathon in Bangalore for qualifying participants, covering production Python, APIs, cloud deployment, LLMs, retrieval, agents, security and client engagement. Its online build phase lists containers, CI/CD and Terraform under shipping and the model layer. For the wider infrastructure path, the DevOps engineer roadmap lists the order of skills.

Frequently asked questions

What is the difference between CI and CD?

Continuous integration (CI) merges changes often and validates each one with an automated build and tests. Continuous delivery extends it by automatically deploying every change to a testing or production environment so it can be released at any time. Continuous deployment goes one step further and releases every change that passes all pipeline stages without human intervention.

Is GitHub Actions free?

GitHub Actions is free for standard GitHub-hosted runners in public repositories and for self-hosted runners. Private repositories receive storage and a monthly amount of included compute usage that depends on the account's plan, and extra usage is billed. GitHub changes plans and included amounts over time, so its billing page holds the current terms.

What is a CI/CD pipeline stage?

A CI/CD pipeline stage is one step in the automated path from a code change to a release, such as build, test or deploy. GitLab's documentation names build, test and deploy as typical stages, and the Jenkins handbook shows a pipeline with Build, Test and Deploy stages. GitLab's documentation adds that stages define the order of execution.

Which file defines a GitHub Actions workflow?

A workflow is a YAML file stored in the .github/workflows directory of a repository. It names the events that trigger it, such as a push or pull request, and one or more jobs, each made of steps that run scripts or reusable actions. A repository can hold several workflow files, each doing a different task.

What are DORA metrics?

DORA metrics are the software delivery measures published by the DORA research programme: change lead time, deployment frequency, failed deployment recovery time, change fail rate and deployment rework rate. They are grouped as throughput and instability, and teams track them over time to see whether their delivery pipeline is improving.

Next step

Copy the sample into a small repository, push it, and watch the test job run, then break a test on purpose to see the deploy job stay skipped. For the engineering path that includes containers, CI/CD and Terraform in a programme setting, see the Forward Deployed Engineer (FDE) Cohort page.

×

Our Courses

Practice-Based Learning Tracks, Supercharged By A.I.