Published July 24, 2023
by Doug Klugh
Always Ready for Primetime
Promote continuous delivery by always keeping your software in a releasable state. Gain high confidence that your software works by integrating changes continuously, while leveraging a deployment pipeline to ensure that your software build is always releasable. Do not isolate new development within feature branches. A better way is to integrate those changes earlier by applying them directly on the trunk with a mature suite of tests to protect the build. Employ DevOps patterns to always keep your software releasable in the midst of constant change.
Here are several strategies that can be used to keep your software in a releasable state.
Maintaining releasable software can be easily achieved amid continuous change by simply hiding the Work in Progress (WIP), such as new feature development, enhancements, or bug fixes. Feature Toggles enable your team to hide functionality that is incomplete while integrating that half-baked code with the production code on the trunk. Another method is to simply restrict access to the entry point by holding back specific URIs or UI components. This will prohibit users from accessing functionality that is not yet ready for release while providing your team the opportunity to test that functionality in production.
(I hope) It goes without saying that you should never rely on production-level testing to verify implementation and validate functionality. All testing in a production environment should always be manual validation with a high degree of awareness that you are testing in production and all transactions, changes, etc. will become part of the production record. Automated tests should never be run in production due to potential side effects and performance impacts. Actually, automated tests should never even be deployed to a production environment. Employ the Test Automation Pyramid in lower environments and treat in-production testing as nothing more than bonus validation.
You can enable testing in your production environment by leveraging Dark Launches, Blue-Green Deployment, Canary Releases, and Ring Deployments. Restrict access to new functionality to enable your team and select (beta) customers to test in production. It’s a good way to give a final stamp of approval and establish high confidence that new functionality will work as expected in the actual production environment.
Take an Agile approach to software delivery by making large changes as a series of small, incremental changes. Not only will Small Stories make hiding WIP easier, it will also promote frequent feedback, facilitate early error detection, reduce variability in workflow, and increase quality — all of which helps to keep your software in a releasable state.
Although Branching by Abstraction1 is not really branching in the traditional sense, this method does enable your team to isolate new functionality from existing production code.
By encapsulating existing functionality within an abstraction layer (or wrapper), you can build new functionality alongside the old (encapsulated) functionality and create a feature toggle to control which implementation path is used. Once the new functionality is complete, you can remove the old code, along with the abstraction layer.
The hard part is isolating the entry points to the existing functionality. Although this should be easy for a well-factored, polylithic architecture, it can be challenging for applications that are more monolithic in nature.
To isolate low-level code, the Strategy Pattern can be used to encapsulate functionality within interchangeable classes while promoting polymorphism. Or the Facade Pattern can be used to create that high-level abstraction for existing functionality and enable clients to utilize the functionality through an interface of the abstract façade class without knowing which implementation is being used.