While branching practice does vary quite a bit, there's a typical set of practices that many of us follow:
- We develop multiple features/stories/etc. in parallel on the "trunk" (or "mainline") of the codebase
- At the end of the cycle (iteration, release, etc.) we "tag" the trunk
- Many times, if preparing for release, we'll branch the trunk to do "hardening" of the release during some type of QA cycle
- Following the release, we'll merge our branches changes back to the trunk
- Later, if defects are found in production, we'll branch again to fix the defect and then merge the fixes back to the trunk
This style of development works very well in a time-boxed style of development. With time-boxed development, we select a period of time, usually one to four weeks, and commit to deliver a set of features/stories during that timebox. XP and others call these iterations, while Scrum prefers to call them "sprints." Regardless of the name, you'll deliver a certain set of functionality to your customers (or, perhaps, yourselves for internal releases) each timebox. Well, that's what will happen in an ideal world.
The world of software development, however, is far from ideal. Things can go wrong during the iteration that can and will upset this branching style:
- Suppose we've committed to delivering a feature and started coding it. Like any good agile developer we've been checking in freely. Unfortunately, we hit a roadblock and it's clear that the feature won't be ready at the end of the timebox. What to do with all of that code that's already checked in? Rolling it back won't be trivial.
- Suppose we have a checked-in, "completed" feature that we determine has a defect that makes much of the system unstable. What to do?
- Suppose we're working on code that's core to the application and two different features that we've committed to lead to an impasse in how they'll affect that core code. Who wins?
- Suppose that the business gives us change in priority. Yes, I know, you're not supposed to do that during the time-box, but what if something happens that totally throws out the value of everything in the timebox? Do we press on and deliver work that we know won't help?
After fighting the good fight for time-boxed development for a few years, our team has decided to move to continuous flow managed via a Kanban system. Our intention is to release every feature to a staging environment as it is completed. Within the staging environment our team of business analysts will verify that the feature meets their needs and signal a go/no-go for production release. All of this is highly automated and nearly happens at the push of a button.
We enable this way of working by creating a branch for each feature that we're implementing. Work proceeds normally within that branch, with the developer or feature team checking-in code at-will. Once the feature is complete and tested, it is merged into the mainline. The mainline is then deployed and the full suite of regression tests are run. Assuming these all pass, the feature is checked-in to the mainline and deployed. What does this give us in the face of the four issues that I identified?
- Since we don't commit to delivering a set of features, but rather individual features, a certain feature being incomplete when we reach a planned release date is completely inconsequential. That code remains safely hidden in its feature branch.
- Features with defects also lead to no worries. With multiple QC checks before code reaches the mainline, we're confident that the mainline is always stable for deployment.
- Core code can evolve freely in this scenario. Yes, there will be a certain amount of merge pain when we bring these two lines together, but a feature that's DONE can always go LIVE, even if a competing feature isn't.
- If the business changes priority, we can easily drop what we're doing and open new feature branches for the expedited request. Again, our mainline is safe and ready for the newest high priority.
We've been working in feature branches now for a few weeks and it has greatly increased our ability to respond to the business, try otherwise "high-risk" experiments, and create a steady flow of value into production. Your mileage may vary, but I highly recommend the practice.