This post is me trying to formalise the internal decision-making process which kicks off when I start on a piece of software development work.
If it was not obvious, this is not a recommended approach, just me trying to make sense of my own (often inconsistent) logic by writing it down.
Some ad-hoc definitions
A feature is made up of releases and should deliver clear value.
A release is made up of changes and is an independently deployable unit, ideally delivering its own value.
A change is made up of assets and represents an atomic modification of the codebase.
An asset is a single object under modification such as a file.
“Forward” is short-hand for Asset -> Change -> Release -> Feature
“Backward” is short-hand for Feature -> Release -> Change -> Asset
Here are some of my day-to-day heuristics when developing software. A lot of these are influenced by ideas from continuous delivery and extreme programming.
Assets = Changes = Releases = Features = 1 is ideal, but uncommon
If I can do this, I’ll probably be happy for the rest of the day.
It means I’ve been able to make a small valuable change without impacting anything else in the system.
Large “Assets per Change” is a possible symptom of tight coupling
If I have to change a lot of files to do a single “thing” then there’s probably a tight dependency between components that I’m changing.
In this case, I’ll tend to push this forward and make multiple intermediate commits instead. This might be something like introducing a new interface or altering a layer of abstraction.
The effect of pushing forward is that we end up with more changes than we would have originally had, but they are individually easier to follow and may in fact reduce the total number of lines changed.
Large “Changes per Release” is a signal for me to break down my work
If I get to 10+ changes per release - in my case, commits per pull-request (PR) - then I take that as an indication that I could be breaking my work down further by pushing it forward and releasing what I have now.
There are cases where this doesn’t apply of course, and sometimes you just need to Do The Thing in one commit, but this is more the exception rather than the rule.
There’s a sweet spot for “Assets per Change” and “Changes per Release” that depends on the overheads in your particular context
While pairing is my preference, our team by default uses a PR-based workflow. Each PR must be approved by at least one other developer.
In this scenario, both “all changes in one commit” and “one commit per edit” are hard-to-review cases.
One of the things I try to optimise towards, if I can, is reviewability - the right number of files per commit, and the right number of commits per pull request such that it’s easy for my teammates to understand my change (both right now and looking back).
Prefer fewer “Changes per Release” to fewer “Releases per Feature”
Bunching up many changes into a release increases the risk and shifts value backwards, away from some desirable end-state like “running in production”.
An (arbitrary) example to illustrate
A feature will require me to modify 100 files.
How best to break this down as a ratio of Assets : Changes : Releases ?
- 100:1:1 - Not Good - a single change of 100 files is likely hard to review / understand
- 10:10:1 - Okay - a single release of 10 changes, each of 10 files, but still a risky all-in-one release
- 5:5:4 - Good - 4 releases of 5 commits, 5 files each - much better, and gives the opportunity to shift the valuable parts of the feature forward and deliver value early
A fairly good rule of thumb, I’ve discovered, is to have roughly the same quantity of each type of work if the overheads in your own context allow it.
Consolidating all the work into 1 (or even 2) types can cause issues - 100 releases of 1 file each sounds like a nightmare!
I’ve pulled some numbers on these ratios from my own work, which I’ll probably publish in a future post.