Feature branching is a technique to work on multiple units of work in parallel. Say we have a sales application and we’re in the process of working on features to support selling cars, bikes and boats. And we’re working on each of these in parallel, likely over an extended period of time:
So we have three feature branches in development. Of course, when these become long lived feature branches as most feature branches tend to, it’s likely conflicting work will take place across branches and remain unreconciled for weeks and perhaps even months. Naturally this causes problems, just imagine three lawyers working independently to modify a 100 page legal agreement over the course of a month.
Many people, for good reasons, avoid long lived feature branching. It’s simply not worth it to multitask development. Instead, they work on one thing at a time, as a team. Or they do some level of multitasking but they use alternatives like feature toggles to disable new functionality until it’s ready. That way, everyone works on one version of the application and continuously integrates their work. I’ve said it before and I’ll say it again, you aren’t continuously integrating if you work with long lived feature branches. A branch is the very definition of an isolated stream of work that’s not integrated.
What’s interesting, even if you avoid the practice of branching your development into features, merely developing the next version of your application is itself a feature branch. Your live version of your application is the one golden version of the truth. It’s the base upon which you merge subsequent new features, regardless how you develop them:
So even if you develop sequentially, one thing at a time, you still have a feature branch for your development work, it’s the next set of features that will be released. If you develop all three new features sequentially and then release them, this is what that looks like in terms of the branches of your application:
It’s important to look at even a single line of development as a feature branch, because all of the potential problems with feature branches, don’t just go away by not developing things in parallel. What are some of those concerns?
- Parallel, isolated work means the chances of conflict increase with the amount of time that passes. Now, surely you’re not making many changes to your release branch, but it is possible that you’ll have the occasional bug fix. That is a change to what’s released and it has to be merged back to development. Many people struggle with how to best handle this because they don’t have the right mindset of development being a feature branch. It’s important to merge those release fixes into development as soon as possible.
- The greater the amount of work in a branch, the greater the risk in merging the functionality And by this I don’t mean in terms of a merge conflict syntactically, but semantically in how the system works. Releasing a line of development is effectively merging the semantics of that development with the users of the application. You may not have syntax conflicts because no real work is happening in the release line, but you have users that understand how the system works and you are potentially breaking that understanding, exponentially so with the amount of change.
- Fear, uncertainty, lack of confidence – Because semantic risk rises with the amount of change that has taken place, people are apprehensive to release the application. Or I think a better way to think of this, people are apprehensive to merge their work in development with the system that users depend upon.
- Double checking and triple checking your work – And because the risk rises, we have a tendency to obsess over checking our work. To a degree that we wouldn’t do if we didn’t develop isolated for such long periods of time.
- We take fewer reasonable risks – Because of the inherent risks in a long lived development branch, we tend to not want to compound our risks. We avoid taking risks, even reasonable risks. Risks like refactoring to improve the system. Or, small changes that might lead to significant dividends for users. We tend to obsess over knowing every minutiae of risk instead of relying on trying simple things based on intuition and seeing what happens.
- We don’t work as cohesively with our users – Users have no idea what we’re up to because we work for weeks and months in isolation. They have less of a partnership mentality and more of an us (users) versus them (developers) mentality. Developers tend to throw releases over the wall and move on to another two months in isolation.
Take a minute and think of some of the problems you’ve had with feature branches in the past. Then, consider how those problems map to a paradigm where you look at development as a feature branch. I think you’ll find even more problems than the ones I’ve listed above.
So what do we do about this? We need to make changes and we can’t just develop in production. The key is the duration of the branch. Most of the risks inherent in branching are proportional to the duration of a branch, or more specifically the amount of change. To combat the risks in your development branch, make it a short lived branch. That means continuing to work on one thing at a time, but merging your work frequently. Which means releasing your work frequently. That doesn’t mean you don’t have some work that you merge that’s disabled, hidden behind feature toggles. It just means you continuously integrate into your actual system, on a reasonable cadence to minimize unnecessary risk. That might look more like the following: