In my last post I illustrated the advantages of short consistent release cycles but in order to achieve this, you often need to build your software in a way that helps ensure that releases cannot be missed.
- Support enabling / disabling features without deployment or rollbackHaving atomic feature configuration for new or risky features reduces the need for a rollback, helping safely deploy new or risky changes. This prevents the friction caused when a deployment containing 5 new features fails because of an undiscovered bug being found in a single feature. Rather than rolling back the code, costing you business confidence and a disrupted development flow, instead prefer disabling the feature and taking a story into your current iteration to fix the problem. You’d be doing this anyway, but rather deliver value with the other 4 features while you’re diagnosing the problems.
- Use tiny, frequently integrated feature branches to isolate development of features.Any form of long running feature branching should definitely be considered an anti-pattern because any un-integrated changes that diverge from your trunk can potentially cause problems with other features. However, small and short lived feature branches consisting of a day or half a days work on a single atomic feature can help you compose a build of integrated features excluding one that is causing problems in QA.
- Stick to MVP: Always cut features, never change your release dateThe temptation to fire fight a broken feature in a release is always strong, but it’s often the core reason of a delayed release. Frequently developers and product managers will want their new shiny feature in the release it was expected and will operate under the belief that “holding the release for an extra day” is the right thing to do. It’s never the right thing to do.By doing this, you prevent any other features in the release from delivering business value on time, and this is always the wrong decision. It’ll hurt, but you should always disable the feature and release, or compose a new build without that feature integrated to keep the steady canter of your release and development cycle.
If you don’t do this, your next iteration will slip by the time it takes to fix the issue you’re currently facing. Rather, prefer pulling the feature and adding a story to fix it at the top of the next iteration.
It’s a much more transparent and accountable way of dealing with slippage, otherwise people will expect a larger delivery than is possible come your *next* release, costing you trust.
- Deployment as a first class citizen: Automate your deployment and rollbackAs you tighten your development and release cycles, any form of manual deployment will become a chore on your development and operations team, possibly counterbalancing the benefits of a tighter focus on releasing. You should always treat deployment and installation as a first class citizen. All your software, from day one, should support continuous integration and automated deployment. If you configure software with these characteristics when it’s new and inert you can prevent any large arduous tasks configuring it later.
- Configure old and new behaviour side by sideIf you’re touching particularly sensitive or high traffic portions of your software, ensure you can switch the behaviour between the two implementations trivially and without rollback. Remove the configuration when the new code is “trusted” to prevent costly rollbacks and patches.
- De-scope existing bugs in order to shipIf an existing bug is threatening your release, de-scope the bug fix to the next release or hotfix it later. Always ship if you’re adding value rather than wait for unconfirmed fixes.
Always prefer de-risking releases for the sake of consistency, it’s a more honest way to deliver software on time.