Continuous Integration (CI) encourages developers to share code changes frequently. The automated build and test process checks that these changes work together to detect errors before they become production issues for customers.
Continuous deployment takes this practice one step further by automatically deploying new builds into a staging or production environment. This reduces friction in the release process and allows teams to deploy code whenever they want.
Automated Testing
A well-designed continuous integration process focuses on automated testing and provides developers with feedback that they’re working on the right thing. However, CI alone doesn’t ensure that the build can be deployed to production without risk.
Continuous deployment takes CI one step further by automating the process of deploying code to a production environment after it passes automated tests. The goal is to reduce friction in the deployment or release process so that application changes can be pushed to production reliably at any time, even while the development team continues to work on new features.
This approach can be achieved using a variety of techniques. The most common is blue-green deployment, which involves maintaining two sets of production-capable environments. Code is pushed to the inactive set for testing and when ready for release it’s swapped over to the active set. This eliminates the need for long-lived development branches in a repository and allows for major refactoring with minimal downtime by building out the new implementation behind an abstraction layer.
While continuous deployment requires a high level of confidence in your automated testing and pipelines, it offers significant benefits for teams that adopt it. For example, if a test fails it’s often easy to pin down where the problem lies because there’s usually only a small number of changes affecting the result. Developers can also work with less context switching and get real-time feedback on the state of their build, which helps them to be more productive and prevent burnout.
Detecting Bugs
When every change to the codebase is automatically tested it reduces the risk of bugs and errors. This means that the time it takes to fix those bugs is significantly reduced too, as teams don’t have to wait for someone to discover a problem before addressing it.
Ideally, each developer should only commit their work to mainline once it’s passed a test, and that should be done on the integration machine. This ensures that any changes to the codebase are caught and fixed quickly – and also that developers can’t just keep adding more and more work on top of a flawed foundation and end up making things even worse.
This approach also makes it much easier to track down problems when they do occur. If a build fails then it’s only the latest version that has that bug and it’s usually pretty easy to find what caused it. A common objection to continuous deployment is that if developers are releasing so frequently then they may be leaving users exposed to half-baked or unfinished features. However, that can be solved by using feature flags so developers only release their code to production once it’s finished.
Getting continuous integration and continuous deployment right requires collaboration between teams, a culture of working together and automation wherever possible. It’s a powerful technique that can dramatically speed up development cycles and help your team deliver stable software to your users faster.
Deployment Pipelines
The deployment pipeline is the process that automates the steps between a developer checking code into the repository and having well-tested, functional builds deployed to production. The first step in a pipeline is to compile the source code into a build. This can be done manually or automatically using a self-hosted CI server, GitHub Actions or a third-party option such as CircleCI. The build is then tested by running an automated test suite. This can be unit tests, integration tests or functional tests that verify how the various components of an application interact with each other and if they behave as expected by end users.
Once the build passes all testing stages, it is promoted to a staging environment that closely resembles the production environment and finally deployed. This is a critical part of Continuous Deployment because it ensures that the changes have been thoroughly tested in an environment that mimics real-world use conditions. This removes any risk of the release breaking the software and allows teams to hit deployment dates more reliably.
Ideally, each stage in the deployment pipeline should be completely automated. This eliminates any manual intervention and makes the entire process faster. Developers can develop faster and releases are made in small batches that are easier to troubleshoot if there is a problem with a particular release.
Feature Flags
Feature flags allow developers to modify the behavior of a system without making disruptive code changes. This allows teams to deploy new features in increments and roll back non-critical ones if they don’t perform well without the overhead of complex release management or lengthy code pushes. It also reduces the stress of a release by lowering the Mean Time to Remediate (a key DORA metric). For example, during Black Friday and other periods of heavy traffic, engineering teams can disable non-critical features to mitigate the load on the system or switch to a lighter version of the website.
Besides decoupling deployment from releases, feature flags help control who gets to see a feature and at what point in the release cycle. They can be used to test a feature with a small subset of users for feedback before it’s rolled out to everyone or to align the release with a marketing campaign.
However, feature flags can create their own issues if not managed properly. They can lead to messy Toggle Point code in the codebase and can be difficult to maintain, especially when they’re long-lived. Savvy teams look at their Feature Flag inventory as assets with a carrying cost and work to keep it low. They’ll use a naming convention and a feature flag platform or service to manage their inventory and make it easy for the whole team to find and understand their toggles.