Maintaining parallel releases simultaneously has implications for how daily development is done. In particular, it makes a discipline that would be recommended anyway practically mandatory: have each commit be a single logical change, and don't mix unrelated changes in the same commit. If a change is too big or too disruptive to do in one commit, break it across N commits, where each commit is a well-partitioned subset of the overall change, and includes nothing unrelated to the overall change.
Here's an example of an ill-thought-out commit:
commit 3b1917a01f8c50e25db0b71edce32357d2645759 Author: J. Random <jrandom@example.com> Date: Sat 2022-06-28 15:53:07 -0500 Fix Issue #1729: warn on change during re-indexing. Make indexing gracefully warn the user when a file is changing as it is being indexed. * ui/repl.py (ChangingFile): New exception class. (DoIndex): Handle new exception. * indexer/index.py (FollowStream): Raise new exception if file changes during indexing. (BuildDir): Unrelatedly, remove some obsolete comments, reformat some code, and fix the error check when creating a directory. Other unrelated cleanups: * www/index.html: Fix some typos, set next release date.
The problem with it becomes apparent as soon as someone needs to
port the BuildDir
error check fix over to a
branch for an upcoming maintenance release. The porter doesn't want
any of the other changes — for example, perhaps the fix for ticket
#1729 wasn't approved for the maintenance branch at all, while the
index.html
tweaks would simply be irrelevant
there. But she cannot easily grab just the
BuildDir
change via the version control tool's
merge functionality, because the version control system was told that
that change is logically grouped with all these other unrelated
things. In fact, the problem would become apparent even before the
merge. Merely listing the change for voting would become problematic:
instead of just giving the revision number, the proposer would have to
make a special change branch just to isolate the portion of
the commit being proposed. That would be a lot of work for others to
suffer through, and all because the original committer couldn't be
bothered to break things into logical groups.
The original commit really should have been
four separate commits: one to fix issue
#1729, another to remove obsolete comments and reformat code in
BuildDir
, another to fix the error check in
BuildDir
, and finally, one to tweak
index.html
. The third of those commits would be
the one proposed for the maintenance release branch.
Of course, release stabilization is not the only reason why having each commit be one logical change is desirable. Psychologically, a semantically unified commit is easier to review, and easier to revert if necessary (in some version control systems, reversion is really a special kind of merge anyway). A little up-front discipline on each developer's part can save the project a lot of headache later.
One area where open source projects have historically differed from proprietary projects is in release planning. Proprietary projects usually have firmer deadlines. Sometimes it's because customers were promised that an upgrade would be available by a certain date, because the new release needs to be coordinated with some other effort for marketing purposes, or because the venture capitalists who invested in the whole thing need to see some results before they put in any more funding. Free software projects, on the other hand, are concerned with maintaining a cooperative working atmosphere among many parties — some of who may be business competitors with others — and the preservation of the working relationship is more important than any single party's deadlines.
Of course, many open source projects are funded by corporations, and are correspondingly influenced by deadline-conscious management. This is in many ways a good thing, but it can cause conflicts between the priorities of those developers who care about a particular release date and everyone else. The developers who are under pressure will naturally want to just pick a date when the release will occur and have everyone's activities fall into line. But the rest of the developers may have other agendas — perhaps features they want to complete, or some testing they want to have done — that they feel the release should wait for.
There is no general solution to this problem except discussion and compromise, of course. But you can minimize the friction by decoupling the proposed existence of a given release from the date when it would go out the door. That is, try to steer discussion toward the subject of which releases the project will be making in the near- to medium-term future, and what features will be in them, without at first mentioning anything about dates (except for rough guesses with wide margins of error).[115] By nailing down feature sets early, you reduce the complexity of the discussion about any individual release, and thus improve predictability. This then creates a kind of inertial bias against anyone who proposes to expand the definition of a release by adding new features or other complications. If the release's contents are fairly well defined, the onus is on the proposer to justify the expansion, even though the date of the release may not have been set yet. Once the release's contents have been defined, discussion about dates will be much easier.
An alternative strategy for dealing with the tension between project release timing and corporate needs is for the company to simply make separate interim releases for its customers. As discussed in the section called “The Economics of Open Source”, such releases can be public and open source, and won't do the project any harm as long as they are clearly distinguished from the project's official releases. However, maintaining separate release lines independently from the project involves overhead in tracking changes and porting them back and forth. This technique only works when a company can dedicate enough people to release management to handle that overhead.
It is crucial, of course, to never present any individual suggestion or decision as written in stone. In the comments associated with each assignment of a ticket to a specific future release, invite discussion, dissent, and be genuinely willing to be persuaded whenever possible. Never exercise control merely for the sake of exercising control: the more deeply others feel they can participate in the release planning process (see the section called “Share Management Tasks as Well as Technical Tasks”), the easier it will be to persuade them to share your priorities on the issues that really matter for you.
The other way the project can lower tensions around release planning is to make releases fairly often (even if you're not using a time-based release scheme — see Time-Based Releases vs Feature-Based Releases). When there's a long time between releases, the importance of any individual release is magnified in everyone's minds; people are that much more crushed when their code doesn't make it in, because they know how long it might be until the next chance. Depending on the complexity of the release process and the nature of your project, somewhere between every three and six months is usually about the right gap between releases, though maintenance lines may put out micro releases a bit faster, if there is demand for them.
[115] Or you could consider doing time-based releases, as described in Time-Based Releases vs Feature-Based Releases.