Set your new features free with flags
Posted on: 23 February 2023
Two big issues plague software development teams, and funnily enough they can seem at odds to each another. The first, the more obvious, technical debt. Old code that’s grown stale and probably wasn’t written by anyone still working on the codebase. No one’s sure what it does or whether it’s still necessary, but it still “works” and certain tests require it to be there to pass.
The second: unresolved features. We have this problem in my small team at work. Features that are worked to near completion, get code-reviewed, go through several iterations of amends, before awaiting final sign-off. But things happen. Priorities change. Clients aren’t coordinated and for reasons out of our control, the pull request just sits there.
New, unreleased code is almost as bad as technical debt. It has potential value, but is becoming less relevant and more out of date with each passing day. But whilst it’s sat un-merged on a branch, it is providing zero value.
I’ve talked previously about Code Review Headaches (another reason feature PRs can stall), and how best to avoid them. My advice in that post was simple:
Features should be atomic and small. They shouldn’t be intertwined with layers upon layers of business logic and beurocracy. A feature being stalled because a client isn’t sure on 5% of it is a poor reason not to begin getting value out of that feature. And sometimes value can just mean battle-tested in production.
Even with the best of intentions in the test suite, things go wrong in production. It is best to weed those niggles out early before that feature has hit launch day. That is definitely not when you want to be fighting obscure bugs in the database.
Feature flag it
So we want to keep those feature PRs small, atomic, well-tested, well-reviewed and most importantly: in production. But how do you do that if the feature isn’t “ready” to be put in front of end-users? Put it behind a feature flag. There’s numerous implementation specifics on how to do this depending on your programming language and software stack, so I won’t go into detail in this post.
But the essence is code can be live, usable, running, without being apparent or accessible to its end user. All the while any integration details you overlooked, sneaky performance issues or dry-run tests can be ironed out before the real users come to play.
All that is required is a small toggle somewhere in your application which determines if this code should be “visible”, and a way to toggle it without a deployment. 95% of a feature can be usable but if that 5% that the end user uses to interact with the underlying code is inaccessible, it’s as good as not there to the end user.
The paradox
I mentioned the conflict between technical debt and unresolved features. If we merge “unfinished” features into production that are never enabled and properly used, doesn’t this become technical debt? The short answer is yes, it can.
But there’s nuance to this game.
If you’ve built a robust, well-tested, reviewed feature that enhances the product and you’ve grown quite fond of; it is a waste of everyone’s time to let this grow stale in the graveyard of un-merged PRs. This has potential future value and should be incorporated into the codebase.
When asked “hey, did we ever finish Feature X, I have a use for it now?”, it’s much better to respond with “yeh, it’s live, just need to enable it and add Y then it’s good to go”; than have to dig out the PR, merge in the main branch, resolve any conflicts, hope the tests still pass, deploy the feature, test it on production, all before entertaining any amends to the original requirements.
There’s a chance it may never be used and become technical debt no one has a clue about. But if your company is run half-decently, chances are you don’t make a habit of writing code that never sees the light of day. Sometimes this code just needs a push, if you’ll pardon the pun. Features are written because they solve business problems. Once that feature is polished, get it live and get it breathing in production, even if its end user isn’t ready for it yet.