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:
-
Code Commit
Developers push their code to a central repository (e.g., GitHub, GitLab, Bitbucket). -
Build & Test
The code is built, dependencies are installed, and automated tests are executed. -
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. -
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 themain
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 runsrules
: Modern alternative toonly/except
for more complex conditionsneeds
: Run jobs in parallel, even across stage boundariesvariables
: 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: