Let’s talk first about traditional shrinkwrap software. This is software which is licensed to be installed on the end user system. The “shrinkwrap” name sounds funny today because most software of this type now gets delivered by download over the Internet with no physical packaging at all. But not that long ago, a lot of software was actually pressed onto CD, placed in a box, and wrapped in cellophane. Many people still use the “shrinkwrap” term for this kind of software even when plastic wrap is not involved.
Even without physical packaging, every release of shrinkwrap software involves a significant amount of overhead for both the developers (updating all the supporting materials and systems) and users (installing everywhere, learning the new features, etc.). Because of this overhead, shrinkwrap software tends to get released every 3-24 months, with annual releases being a typical situation.
Figure 11.2, “Traditional Shrinkwrap” illustrates the development of four major releases, along with several maintenance releases. In this development process, the team is working on two or three releases at the same time. Time goes from left to right in the picture.
First, after a bunch of work gets done, version 1.0 is released.
Version 2.0 development begins immediately, using 1.0 as the starting point.
Version 3.0 development begins early, branching off the 2.0 code base before it is complete.
To deal with a critical bug-fix, version 1.0.1 is released.
Version 2.0 is completed and released. Stuff from 2.0 gets merged into the 3.0 branch. For users still on v1, a bug-fix version 1.0.2 is released.
To deal with a critical bug-fix, version 2.0.1 is released.
Version 3.0 is completed and released. Bug-fix releases are done for users still on v2 and even on v1. V4 begins with 3.0 as the starting point.
Version 4.0 is completed and released. Once again, there are bug-fix releases for the two previous versions, resulting in 3.0.1 and 2.0.3, but v1 is too old for continuing the maintenance.
Let’s talk specifically about how we can use branches to support this kind of workflow. First, we have our main development branch. We’ll call it Master. All of your development work goes here. All new feature work goes in here, and bug-fixes from other branches get merged back into here. Everything finds its way to Master, directly or indirectly. I define my workflows such that Master is usually regarded as “somewhat unstable”.[37] Insofar as lots of developers are using it, we want its contents to build and pass test suites. But this is where work-in-progress goes.
As development moves along, at some point it is time to begin preparing for a release. This phase of the cycle is often called “QA” or “Testing”, but I prefer not to use terminology which suggests that all testing and bug-fixing is left until the end. Best practices in software development are to find and fix bugs as early as possible. So I call this phase “polishing”, which is more suggestive of taking something that is basically done and giving it the detailed attention it needs to have a really fine fit and finish.
The process of polishing your software to make it ready for release is mostly about fixing minor bugs. But sometimes during this phase, stuff happens that should not go into the release.
Sometimes we identify bug-fixes or improvements that are too risky to be included in the release being polished[38].
Sometimes feature work on release N+1 begins sometime during the Polishing phase of release N.
Sometimes, even though we spent some time during the release N cycle building a feature, we decide that feature needs to get postponed until release N+1.
If none of the above happened, and the Master branch contains exactly what will go into the release, and absolutely everything that needs to be committed during the Polishing phase can go into the release currently being Polished, then we don’t need a branch.
But that’s rare. So we need a branch. We need a way to keep the bug-fixing and polishing of the release separate from everything else.
So we create a new Polishing branch.
Everybody working on this release should switch and start working in the Polishing branch. All bug-fixes for the release should be committed directly into the Polishing branch, not into the Master branch. They will get merged back into Master later.
Anything which needs to be committed but does not go into this release should go into Master.
Polishing is a short-lived branch. It exists entirely for the purpose of getting something ready for release. When that something is ready, the Polishing branch is closed.
When the polishing is done and the software is nice and shiny, it is time for release.
There are three things that happen at Release time.
Create a Release branch off the Polishing branch.
Merge all the changes from the Polishing branch back into Master.
Close the Polishing branch.
You should always have a branch which contains the exact contents of your current release.
Actually, the Release branch almost doesn’t need to be a branch. In a perfect world, the release is flawless and this branch will never get any more changes committed to it. A branch which never gets any commits is effectively a tag.
But in reality, critical fixes are sometimes necessary. Users don’t always upgrade to the latest release and it is customary to continue providing a certain amount of support and maintenance for older releases, within reason.
Sometimes bugs slip through the Polishing phase and an x.0.1 release is necessary.
Sometimes it is a good idea to do a 1.0.x when 3.0 is released to make their behavior more consistent or compatible in some way.
Sometimes an x.0.1 release is necessary because some other piece of software changed.
When a critical fix is needed, do the fix in the Release branch. Merge it back to Master.
Note that I have been referring to “the” Release branch, as if there is only one of them. In practice, you should keep an open Release branch for every major version which was released to customers. So your release branches will likely be named something like “Release_v1” and “Release_v2” and so on.
Everything I’m saying in this chapter can be considered as a starting point. Your particular situation may be much more complicated. You should feel free to tweak things until you have a workflow that is effective for your team. And make sure you’re using a VCS that is flexible enough to adapt with you.
Here’s an example:
Sometimes development of new features is too complicated to have just one Master branch. In these situations, it may be helpful to think of Master as a cluster of branches. There is still one main branch called Master where everything eventually ends up, but we also have subordinate branches called “Feature Branches”.
With Feature Branches, developers work on each major feature in a dedicated branch. When the feature is done, it gets merged into Master. But it is also possible to construct a Polishing branch directly from one or more Feature branches. This allows us to make the decision to release only a subset of the features which have been under development. For example, in Figure 11.6, “Feature Branches”, features B and C are not ready for release, so we construct a Polishing branch which contains only features A and D.
[37] Some Git developers use its “master” branch in the manner I describe for a Release branch, treating it as highly stable. This is merely a difference in naming.