Skip to main content
CI/CD in Practice

CI/CD in Practice

·5 mins·
DevOps Article
Table of Contents

Introduction
#

Continuous Integration (CI) and Continuous Deployment (CD) are now central components of modern software development. They help us deploy software faster, more reliably, and with fewer manual steps. In this article, we’ll look at how a typical CI/CD process works in practice, which tools we can use, and what steps are necessary to build a pipeline.

Why CI/CD?
#

When we develop software, we want to ensure that changes don’t just work on our laptop, but also run stable in production. This is exactly where CI/CD comes in:

  • CI (Continuous Integration): All code changes are regularly integrated, tested, and automatically checked. Errors are caught immediately.
  • CD (Continuous Deployment or Delivery): Successfully tested changes can be automatically deployed to staging or production environments.

The goal: Short feedback cycles and stable, fast delivery.

Typical CI/CD Workflow
#

A practical CI/CD process can be divided into four main steps:

  1. Code Commit
    Developers push their code to a central repository (e.g., GitHub, GitLab, Bitbucket).

  2. Build & Test
    The code is built, dependencies are installed, and automated tests are executed.

  3. Staging Deployment
    When the tests are successful, the application is deployed to a staging environment. There we can manually check if everything works as expected.

  4. Production Deployment
    Finally, the application is deployed to the production environment – either automatically or after manual approval, depending on the setup.

Pipeline Visualization
#

The following diagrams help us understand the process. First, a classic CI/CD pipeline schema:

flowchart TD
    A[Code Commit] --> B[Build]
    B --> C[Tests]
    C --> D[Staging Deployment]
    D --> E[Production Deployment]

Each step depends on the previous one. If a step fails, the pipeline automatically stops to prevent faulty software from spreading further.

This sequential execution brings several advantages:

  • Early error detection: Problems are immediately identified before they affect downstream steps
  • Resource conservation: Time-consuming deployments are only performed with successful tests
  • Quality assurance: Only tested and validated code reaches the production environment
  • Clear responsibilities: Each step has a defined purpose and success criteria

If a step fails, developers receive immediate feedback with detailed logs to quickly identify and fix the problem.

Example with GitLab CI
#

Let’s look at how a simple pipeline could look in GitLab. In the .gitlab-ci.yml file, we define the jobs:

stages:
  - build
  - test
  - deploy

build-job:
  stage: build
  script:
    - npm install
    - npm run build

test-job:
  stage: test
  script:
    - npm run test

deploy-job:
  stage: deploy
  script:
    - ./deploy.sh
  only:
    - main

Simple Explanation
#

  • stages: Defines the order of steps.
  • build-job: Installs dependencies and builds the application.
  • test-job: Runs tests (e.g., unit tests, linting).
  • deploy-job: Deploys the application once tests are successful and the main branch is used.

Detailed Explanation of the Pipeline Configuration (with Extensions)
#

This GitLab CI configuration shows a simple but effective pipeline structure. Let’s examine each section more closely:

Stages Definition:

stages:
  - build
  - test
  - deploy

The stages define the execution order. GitLab runs all jobs of a stage in parallel before moving to the next stage.

Build Job (in detail):

build-job:
  stage: build
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 hour

The build job should ideally also create artifacts that can be used in subsequent jobs. These are stored temporarily and automatically deleted after one hour.

Test Job (extended):

test-job:
  stage: test
  script:
    - npm run test
    - npm run lint
  coverage: '/Coverage: \d+\.\d+%/'
  artifacts:
    reports:
      junit: test-results.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

Extended test jobs can measure code coverage and visualize test results in GitLab.

Deploy Job (with environments):

deploy-job:
  stage: deploy
  script:
    - ./deploy.sh
  environment:
    name: production
    url: https://myapp.example.com
  only:
    - main
  when: manual

The environment keyword allows tracking deployments in GitLab. With when: manual, the deployment is only executed after manual confirmation.

Additional useful configurations:

  • cache: Cache dependencies between pipeline runs
  • rules: Modern alternative to only/except for more complex conditions
  • needs: Run jobs in parallel, even across stage boundaries
  • variables: Define environment variables for the entire pipeline

Automation and Feedback
#

A crucial advantage of CI/CD is fast feedback. Developers know within minutes whether their code works. Tools like GitLab CI, GitHub Actions, Jenkins, or CircleCI enable us to have:

  • Parallel test runs
  • Notifications on errors
  • Automatic rollbacks on failed deployments

Best Practices
#

For CI/CD to really provide value in practice, we should follow a few rules:

  • Take tests seriously: A pipeline is only as good as the tests it executes.
  • Optimize for speed: Builds shouldn’t take forever – small, modular jobs are better.
  • Automate where it makes sense: Recurring tasks (linting, security checks, container builds) should run automatically.
  • Transparency: Everyone on the team should be able to immediately see the pipeline status.

Conclusion
#

With CI/CD, we manage to deliver software changes safely, automatically, and at high frequency. Getting started is often easier than it first appears: a Git repository, a CI/CD tool, and a few clean test cases – and we can begin.

The biggest benefit is that we catch errors early, get faster feedback, and increase our productivity as a team.

flowchart TD
    Dev["Developers"] --> CI["Continuous Integration"]
    CI --> CD["Continuous Deployment"]
    CD --> Prod[Production]
    Prod --> Feedback["Feedback to Team"]
    Feedback --> Dev

This creates a cycle through which product quality can be continuously improved.

If you have questions or comments about this article, feel free to contact me through the channels mentioned below.


Further Reading:

Timo Staudinger
Author
Timo Staudinger
Senior DevOps Engineer

Related

Blue-Green Deployment with Kubernetes
·5 mins
How-To Deployment Scripting
Minimize risks, maximize availability. Releases with minimal downtime and fast recovery from issues.
Setting up msmtp as an SMTP Client
·7 mins
Mail How-To Configuration Linux
Need to receive server logs and output from failing jobs via email? msmtp makes it easy.
Restic Backups
·6 mins
Backup How-To Scripting Linux
How to create a sustainable backup concept with minimal effort using a shell script.