bigonotes
Engineering PracticesCI/CD and Releases

CI Pipelines

What a CI pipeline does, which stages it runs, and how to keep it fast, reliable and safe to merge on.

6 min readLast updated

Continuous integration (CI) means every developer merges small changes into the main branch often, and an automated pipeline checks each change. The pipeline is a series of jobs that lint, test and build your code on every push or pull request. It finds problems minutes after you write them, not weeks later during a big merge. On most teams, a green pipeline is the rule for "safe to merge".

CI, continuous delivery and continuous deployment#

You'll often hear CI mentioned alongside CD. The three terms sound alike, but each one adds a step on top of the last.

Practice What is automatic Who pushes to production
Continuous integration Build and test on every change Not covered
Continuous delivery Every green build is ready to release A person clicks "deploy"
Continuous deployment Every green build goes to production The pipeline, with no human step

"CD" can mean either delivery or deployment. Ask which one a team means. Most teams start with continuous delivery and move to continuous deployment when their tests and monitoring are strong.

Choosing delivery or deployment#

If your team is deciding between the two kinds of CD, the choice depends on how much control you need over the moment a change goes live.

Continuous delivery Continuous deployment
Last step A person approves the release Automatic when all checks pass
Release pace When the team decides Many times a day
Fits Regulated or safety-critical work: banking, healthcare Fast-moving products: e-commerce, social apps, startups
Needs A clear approval step and audit trail Strong automated tests, monitoring and auto rollback
  • Choose continuous delivery when rules or contracts need a human sign-off, a change record or a fixed release window. The pipeline still builds, tests and stages every change. Only the final push waits for approval, often after user acceptance testing in staging.
  • Choose continuous deployment when fast feedback matters most and a bad change is cheap to undo. Every green build ships, so each release is small and easy to trace.

Continuous deployment doesn't mean skipping staging or safety steps. The pipeline can still deploy to staging, run smoke tests and roll out as a canary. It does all of this without waiting for a person. See deployment strategies.

Typical pipeline stages#

Whichever kind of CD you pick, it sits on top of the same CI pipeline. A pipeline runs cheap, fast checks first and slow, expensive checks later. Each stage is a gate: if it fails, the later stages don't run.

  1. Lint and format check: catch style problems and simple bugs (for example, with ruff).
  2. Type check: find type errors without running the code (for example, with mypy).
  3. Unit tests: test small pieces of code in isolation.
  4. Build the artifact: package the code into one deployable unit, such as a container image or a wheel (a Python package file).
  5. Integration tests: test the artifact against real dependencies, such as a database in a container.
  6. Security scan: check dependencies for known vulnerabilities and look for leaked secrets.

The exact order varies. Some teams run the security scan in parallel with the tests, because the two don't depend on each other.

Fail fast and stay fast#

Ordering stages well is only part of it; the whole pipeline also has to be quick. A slow pipeline changes how people work. If CI takes 40 minutes, developers batch changes, skip runs or merge without waiting. A common goal is to give feedback in under about 10 minutes.

Ways to keep it fast:

  • Order by cost. Put the stages that fail most often and run fastest at the front.
  • Cache dependencies. Reuse downloaded packages between runs, keyed on a hash of your lock file.
  • Run jobs in parallel. Lint, type check and unit tests don't depend on each other.
  • Split slow test suites across several machines (test sharding).
  • Fix or remove flaky tests. A test that fails at random teaches people to ignore red builds.

A flaky test passes and fails on the same code. Quarantine it and fix it soon. Don't paper over it with retries.

Build once, deploy many#

Stage 4 builds the artifact, and one rule decides how you use it. Build your artifact one time, then deploy that exact artifact to every environment. Don't rebuild it for staging and again for production. A rebuild can pull a different dependency version, so the thing you tested isn't the thing you ship.

  • Give each artifact a unique, immutable tag, such as the commit SHA. Immutable means you never overwrite it.
  • Push it to an artifact registry (a container registry or package index).
  • Keep environment-specific settings out of the artifact. Inject them at deploy time with environment variables or a config service.

Branch protection and required checks#

A fast, reliable pipeline still only protects the main branch if people can't skip it. Branch protection rules on GitHub, GitLab and similar tools enforce this.

  • Mark the key CI jobs as required status checks, so a pull request can't merge while they're red.
  • Require at least one approving code review.
  • Block direct pushes and force pushes to main.
  • Require the branch to be up to date with main before merge, or use a merge queue. This catches two changes that pass alone but break together.

Example: GitHub Actions for a Python project#

Here's how several of these ideas look in one file: parallel jobs, caching and fail-fast tests. This workflow runs on each pull request and on each push to main. The lint job and the test job run in parallel. The cache option in setup-python reuses downloaded pip packages between runs.

name: ci

on:
  pull_request:
  push:
    branches: [main]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          cache: pip
      - run: pip install -r requirements-dev.txt
      - run: ruff check .
      - run: mypy src

  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.11", "3.12"]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          cache: pip
      - run: pip install -r requirements-dev.txt
      - run: pytest --maxfail=1 -q

The matrix runs the tests once per Python version, in parallel. --maxfail=1 stops pytest at the first failure, so you get feedback sooner. A real pipeline adds a build job after these two, with needs: [lint, test].

Common pitfalls#

Even with a good workflow file, most CI problems come from habits, not from tools.

  • Ignoring red builds. If main is broken, fixing it is the top priority.
  • Long-lived branches. A branch that lives for weeks isn't continuous integration. Merge small changes daily.
  • Tests that need a shared environment. They interfere with each other. Give each run its own containers.
  • Secrets in logs. Store secrets in the CI tool's secret store, and never echo them.

Key takeaways#

  • CI checks every change automatically. Continuous delivery makes every green build releasable. Continuous deployment releases it with no human step.
  • Order stages from cheap and fast to slow and expensive, and aim for feedback in about 10 minutes.
  • Cache dependencies, run independent jobs in parallel, and fix flaky tests quickly.
  • Build one immutable artifact and promote that same artifact through every environment.
  • Use branch protection and required checks so nobody can merge around CI.
Revision notes · 2 min

Core idea#

Continuous integration (CI) means developers merge small changes into main often, and a pipeline checks each change. The pipeline lints, tests and builds on every push or pull request. A green pipeline is the usual rule for "safe to merge".

CI vs CD#

Practice What is automatic Who pushes to production
Continuous integration Build and test on every change Not covered
Continuous delivery Every green build is ready to release A person clicks "deploy"
Continuous deployment Every green build goes to production The pipeline, no human step

"CD" can mean either one. Ask which one a team means.

  • Choose delivery for regulated or safety-critical work that needs a human sign-off.
  • Choose deployment for fast-moving products with strong tests, monitoring and auto rollback.
  • Continuous deployment can still use staging, smoke tests and canaries, with no human wait.

Key points#

  • Stages run cheap and fast first: lint, type check, unit tests, build the artifact, integration tests, security scan.
  • Each stage is a gate. If it fails, later stages do not run.
  • Aim for feedback in under about 10 minutes. A slow pipeline makes developers batch changes or skip runs.
  • Cache dependencies, keyed on a hash of the lock file.
  • Run independent jobs in parallel, and shard slow test suites across machines.
  • Build once, deploy many. Tag the artifact with an immutable tag such as the commit SHA, and promote that exact artifact.
  • Keep environment settings out of the artifact. Inject them at deploy time.

Branch protection#

  • Mark key CI jobs as required status checks.
  • Require at least one approving review.
  • Block direct pushes and force pushes to main.
  • Require the branch to be up to date with main, or use a merge queue. This catches two changes that pass alone but break together.

Pitfalls#

  • Flaky tests teach people to ignore red builds. Quarantine and fix them; do not add retries.
  • Rebuilding per environment can pull a different dependency version. You then ship something you did not test.
  • Long-lived branches are not CI. Merge small changes daily.
  • Shared test environments make runs interfere. Secrets in logs leak; keep them in the CI secret store.

Say it in an interview#

  • "Delivery means every green build can ship. Deployment means it ships with no human step."
  • "We build one immutable artifact and promote it through every environment."
  • "Required checks and branch protection stop anyone from merging around CI."

Found a mistake? Email ask@bigonotes.com

Further reading#