Continuous integration and continuous delivery — CI/CD — reverse the logic. Releases become small, frequent and automated, and that is precisely what makes them safe.
Continuous integration: one shared, working mainline
Continuous integration means that every developer merges small changes into the shared codebase frequently, at least daily, and that every change is automatically built and tested. Problems surface within minutes, while the change is still fresh in someone's mind, instead of weeks later during an integration phase.
The practical ingredients:
- Short-lived branches that are merged quickly, rather than long parallel lines of work.
- An automated build that compiles, packages and runs tests on every change.
- A rule that the mainline stays green: when the build breaks, fixing it comes first.
- Code review on every change, kept small enough to be reviewed properly.
Continuous delivery: always ready to release
Continuous delivery extends the pipeline so that every change that passes the checks is ready to go to production — deployed automatically to test environments, verified, and released with a single, repeatable step. Continuous deployment goes one step further and releases automatically. Which of the two fits depends on the business context; regulated environments often keep a human approval before production, and that is perfectly compatible with the approach.
A typical pipeline:
- Build and unit tests.
- Static checks: code quality, known vulnerabilities in dependencies, secrets accidentally committed.
- Deployment to a test environment created from the same infrastructure code as production.
- Integration and end-to-end tests.
- Approval, if required.
- Release to production, with automatic checks afterwards.
The test pyramid
Automated tests are what make frequent releases possible, but not all tests are equal. The test pyramid is a useful guide:
- Many unit tests at the base: fast, precise, testing individual functions and rules.
- Fewer integration tests in the middle: components together with a real database, a message queue, an external API or a stand-in for it.
- A small number of end-to-end tests at the top: the critical user journeys through the real interface — log in, place an order, approve an invoice.
End-to-end tests are valuable but slow and fragile; a suite that relies mostly on them becomes a bottleneck. The aim is a pipeline that gives a trustworthy answer in minutes, not hours.
For existing systems without tests, start by writing tests around the parts you are about to change. That is how the safety net grows where it is needed — the same principle behind modernising legacy software step by step.
Environments and infrastructure as code
"It worked in test" usually means test and production are different. Describing infrastructure as code (with tools such as Terraform or OpenTofu, or the cloud provider's own templates) makes environments reproducible: the same definitions create test, staging and production, with differences limited to size and configuration. It also makes changes to the infrastructure reviewable, like code — which is part of every cloud migration we run.
Releasing without fear
Small releases reduce risk; a few techniques reduce it further:
- Feature flags separate deploying code from enabling a feature. New functionality can be switched on for internal users first, then for everyone, and switched off without a new release.
- Blue-green and canary deployments run the new version alongside the old one and move traffic gradually, with automatic rollback if error rates rise.
- Database changes in compatible steps: add the new column, release code that uses both, migrate, then remove the old one. Each step can be released and rolled back on its own.
- Monitoring tied to releases, so that a change in error rates or response times is linked to the release that caused it.
Measuring whether delivery is improving
The DORA research programme, now part of Google Cloud, identified four metrics that together describe software delivery performance:
- Deployment frequency — how often you release to production.
- Lead time for changes — how long a committed change takes to reach production.
- Change failure rate — the share of releases that cause a problem in production.
- Time to restore service — how quickly you recover when something goes wrong.
Their key finding is that speed and stability go together rather than against each other. Measure your own starting point, and track the trend rather than comparing yourself with other organisations.
Where to start
Automate the build and deployment of one application end to end, add tests around the next change you plan to make, and start measuring the four metrics. Every application we build in our software engineering service ships with this pipeline from the first week, so that your team inherits a delivery process, not just code.