Continuous Delivery

Continuous Delivery is an iterative approach to software development where code changes are automatically built, tested, and prepared for a release to production. This practice ensures that software can be deployed quickly and safely at any time, enhancing the agility and responsiveness of the Stream Team.


The purpose of Continuous Delivery is to increase the speed and quality of software delivery by automating the deployment process. Only when customers have a product in their hands will we understand whether it meets their needs. Continuous Delivery enables teams to release small, frequent changes to production, allowing them to gather feedback and make adjustments as needed and reducing the risk of delivering a product that does not meet customer expectations.

Principles and Practices


We don't know how customers will behaveWe need to reduce the risk that customers will not want our solution.By breaking down solutions we can limit the amount of upfront investment needed to validate whether our features are delivering the expected impact.
Not all software designs are equalThere are infinite ways to develop something but not all of them are performant, scalable and maintainable.By using the most appropriate architectural styles for our needs we can ensure that our systems are performant and scalable. But high quality performance also relies on high quality code. By using workflows like Test Driven Development (TDD) and Acceptance Test Driven Development (ATDD) we produce more testable and re-factor friendly code. And by involving multiple people through pairing or teaming we can achieve better designs.
Defects impact on user adoptionWe need to increase our odds of catching failures before they impact customers.By implementing comprehensive automated test suites that are run repeatedly using continuous integration practices we can catch the bugs we can think of. By conducting exploratory testing we can catch the bugs we didn't think of.
System availability impacts on user adoptionWe need to ensure that our products are available and performant and that we can recover from failure quickly when it occurs.By enabling continuous deployment techniques we can achieve zero downtime releases and automated rollbacks that ensure our products are available at all times. By investing in instrumentation and monitoring we can identify the root cause of production issues quickly.
Unused work provides zero effectivenessWe need to limit the risk that work is not released.By implementing work-in-progress (WIP) limits we force teams to work in short iterations and minimise both the risk and scale of unused work.


Changes are expensiveWe need to balance the cost of changes with our tendency to over-engineer solutions.By using modern design patterns and investing in automated tests, including fitness functions, we can make changes easier and safer to implement. This helps us to avoid the big design up front (BDUF) approach that forces us to build complex, expensive, and unvalidated solutions.
Releasing software costs moneyWe need to reduce the cost of releasing software to enable more frequent releases because longer release cycles increase our biggest risk - that customers will not want our product.By implementing continuous delivery and continuous deployment with automated infrastructure and automated rollback processes we can reduce the releasing costs.
Dependencies impact productivityWe need to ensure that teams control their ability to create and release solutions within their value stream.By designing teams so that they have ownership over separate value streams with independent code we can remove the need for coordination and hand-offs between teams. In addition, by ensuring that teams can release independently we can remove the path to production bottleneck.
Large parallel code changes lead to reworkWe need to ensure that everyone is working on the latest code to avoid merge hell.By implementing Trunk Based Development, supported by feature toggles we can ensure that teams have the latest code and can safely merge work-in-progress code frequently without impacting other teams.
Inspection at the end results in reworkWe need to embed best practices in quality, security, legal, performance and other non-functional items while the code is being written.By implementing continuous integration we can catch issues earlier and fix them at the cheapest point.
Context switching reduces productivityWe need to help teams focus on completing one item end-to-end before moving to the next item.By replacing pull-requests, which happen after code is written, with practices such as pairing and teaming we can remove the need for context switching and improve throughput.


Code is a liabilityWe need to achieve the desired outcomes with as little code as possible.By using Wardley maps we can identify commodity elements in our solutions and source off-the-shelf solutions instead of custom building.
Prioritise readability firstWe need to prioritise readability because code is read a lot more than it is written.By implementing good and consistent development standards we can remove stylistic confusion and encourage cleaner code conventions for the remaining code.
Code is an imperfect abstraction of the real worldWe need to ensure that the assumptions that we make that prove false over time (aka. technical debt) do not slow down progress in the long run.By enabling collective code ownership within the team and weak code ownership across teams we give teams the impetus and ability to continuously refactor their code to ensure it remains as simplistic as possible.
People leave teamsWe need to minimise the amount of knowledge lost when people leave the team.By implementing Architectural Decision Records we can keep track of the key decisions made, along with the rationale behind them, ensure that the key knowledge is maintained. And by involving multiple people through pairing or teaming we can transfer knowledge more effectively across the team to limit knowledge leakage.


Despite its benefits, continuous delivery faces several valid criticisms.

CriticismDescriptionMitigation Strategies
ComplexitySetting up and maintaining a Continuous Delivery pipeline can be complex and resource-intensive.Start simple, and incrementally add complexity as needed. Use standardised tools and practices to reduce maintenance.
Over-reliance on AutomationExcessive dependence on automated testing can lead to missed bugs or issues.Incorporate exploratory testing and manual review processes, especially for critical features.
Resistance to ChangeTeams may resist adopting Continuous Delivery or the required development practices such as TDD due to unfamiliarity with the process or fear of increased workload.Provide training, demonstrate the benefits, and phase the adoption to ease the transition.
Deployment Frequency ConcernsRapid deployment can lead to user disruption or fatigue from constant updates.We can separate deployment from releasing using feature flags so that we can deploy frequently but only release when we are ready.


To avoid pitfalls in continuous design, be wary of these anti-patterns:

  • Bypassing the Pipeline: Manually overriding the Continuous Delivery process to deploy code changes, which can lead to untested or unapproved changes reaching production.
  • Ignoring Failing Builds: Continuously moving forward with new development without addressing failures in the build or test stages of the pipeline.
  • Over-complicating the Deployment Pipeline: Implementing too many steps or checks in the pipeline can slow down the process and reduce the benefits of CD.

  • Neglecting Post-deployment Monitoring: Failing to adequately monitor the application in production for performance issues, user feedback, and errors can lead to a degraded user experience.

  • Infrequent Deployments: Not breaking down work into small batches reduces the advantages of Continuous Delivery and can result in batched changes that are riskier and more difficult to troubleshoot.

Was this page helpful?

Invalidated Assumptions
© ZeroBlockers, 2024. All rights reserved.