☁ Cloud & DevOps

CI/CD pipelines: from commit to release with no manual steps

What continuous integration, delivery and deployment are, how the stages of a pipeline are organized and which mistakes to avoid for fast, safe releases.

In many projects releasing is still a ritual: someone logs into the server, pulls the code, runs two or three commands remembered by heart and hopes everything goes well. It works until it doesn't, then one Friday evening a step is skipped and the site stays down for an hour. A CI/CD pipeline turns that ritual into an automatic, repeatable and verifiable process.

Three acronyms to tell apart

The terms are often used as synonyms, but they describe different levels of automation.

  • Continuous Integration (CI): every change pushed to the repository is built and tested automatically. The goal is to find out immediately whether a change breaks something, while it is still small and easy to fix.
  • Continuous Delivery: beyond the tests, the pipeline produces a production-ready artifact and takes it as far as a test environment. The final release happens with human approval, often a single click.
  • Continuous Deployment: the step into production is automatic too. Every change that passes all checks is released without intervention.

There is no single right level. Many companies stop at continuous delivery because they want to decide when to release, for example avoiding peak traffic times.

The stages of a typical pipeline

A pipeline is a sequence of steps run by an automation service, such as GitHub Actions, GitLab CI or Jenkins, whenever something happens in the repository.

  1. Fetching the code and preparing the environment, with the correct language version.
  2. Installing dependencies, preferably from a lock file so the versions are identical every time.
  3. Static checks: formatting, linters, type analysis. They are fast and catch trivial mistakes.
  4. Automated tests: unit tests first, which are quick, then integration tests involving databases or external services.
  5. Building the artifact: a container image, a package or a folder of static files, tagged with the commit identifier.
  6. Security checks: scanning for vulnerable dependencies and searching for secrets accidentally left in the code.
  7. Deploying to staging and quick verification tests on the running system.
  8. Deploying to production, automatically or with approval.

The order is not random: the fastest checks come first, so a bad change is blocked in seconds rather than after ten minutes of tests.

A concrete example

A basic workflow for a Python project on GitHub Actions can be described like this:

name: ci
on:
  push:
    branches: [main]
  pull_request:

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

Every pull request is thus checked before being merged into the main branch. From here you gradually add builds, scans and deployments.

Principles that make a pipeline reliable

Build once, deploy everywhere

The artifact should be built only once and then promoted from one environment to the next. Rebuilding it for production means releasing something different from what was tested, perhaps because a new version of a dependency came out in the meantime.

The pipeline must be fast

If it takes forty minutes to know whether a change works, developers stop waiting and pile up changes, losing the point of continuous integration. Dependency caching, tests run in parallel and separating fast tests from slow ones help keep it under a few minutes.

A randomly failing test must be fixed immediately

Flaky tests, which sometimes pass and sometimes don't, are the worst enemy of CI. They teach people to ignore red and rerun the pipeline until it turns green. A flaky test must be fixed or temporarily quarantined, never tolerated.

Secrets kept safe

Deployment credentials belong in the CI service's secret store, never in the repository. A good rule is to give each pipeline only the permissions it strictly needs and, where possible, to use temporary credentials instead of permanent keys.

Every release must be reversible

Before automating deployment, ask yourself how you go back. If the previous artifact is still available and a release is a simple version switch, rollback becomes a one-minute operation. Database changes deserve special attention, because they cannot always be undone.

Where to start

You don't need to build everything in a day. The step with the best effort-to-result ratio is running the tests on every pull request and blocking the merge if they fail. Then you automate the build, then deployment to staging, and finally production. Each step reduces manual work and the risk of mistakes, and documents in a versioned file how the software is released.

In short

  • CI checks every change; continuous delivery prepares the release; continuous deployment performs it automatically.
  • Order the stages from fastest to slowest to block obvious errors immediately.
  • Build the artifact once and promote it between environments without rebuilding it.
  • Keep the pipeline fast, eliminate flaky tests and protect credentials.
  • Start with automated tests on pull requests and add the other stages gradually.
MM
Marco Marzaduri

Marco Marzaduri works in cybersecurity, digital forensics and web development. With degrees in computer science and psychology, he combines technical analysis with an understanding of the human factor.

Read also