We’re talking about version control here, but we’re also talking about productive habits.
The term that is being passed around is “micro-commit,” and it is one of those concepts that we didn’t realize needed a name or a definition or an article on a blog.
A micro commit is a tiny commit. It consists of the changes necessary to do one tightly-scoped change. Maybe it’s just a file reformat. Maybe it’s just a variable rename. It could be the addition of one loop or one statement. It might involve a new microtest and just enough code to make it pass.
For years we just referred to these as a “commit” not realizing that there were people in the world who were doing many things at once and only committing when some significant piece of work was completed.
Not Everyone Does That?
I was surprised to find out that there are people who only commit maybe once or twice a week. All their code is at the mercy of their editor’s history and at risk of being lost or damaged. It’s unnerving for me to even think that way.
Some time ago, I tweeted that it is better to not mix reformatting, refactoring, and new code all in the same commit. I thought it was obvious how that would work:
- Get a current version of the code. Be sure all the tests pass.
- Reformat the code. Be sure all tests pass. Commit it.
- Do some refactoring. Be sure all tests always pass. Commit it.
- Add some new test, and just enough code to pass the test. Commit it.
- Do some more refactoring. Be sure tests pass. Commit it.
That is how all the professionals I work with have done their work for many years. We’ve been doing this probably since the very early 2000s, maybe the 1990s too, but certainly since the advent of DVCS like git.
Industrial Logic has included the “integration” step of the refactoring process since long before I even joined the company. The tight loop of TDD here is Red, Green, Refactor, Integrate. Integrate is at least to commit, but preferably to pull and push as well (GIT terms for updating the code in the main development branch and sending the changes back to that branch for the rest of the team to use).
The tweet caused some consternation, though. It turns out that not everyone does micro-commits. Apparently, some people only commit when an entire user story, epic, or job is completed.
My assumption was that people commit at least 4 times an hour, and sometimes as many as 12 or 15. In my context, the advice is good and reasonable.
If you only make huge commits, then my advice sounds like “do all your work without reformatting or refactoring and then, perhaps weeks or months in the future, as a separate task, do some refactoring or reformatting.”
Do One Thing vs While You’re At It
I realized that I had the advantage of having had good coworkers and mentors who have always appreciated the value of taking tiny steps.
I was lucky enough to spend most of my programming life knowing what has become known as “Curly’s Law”: “One thing. Just one thing. You stick to that.”
“Doing only one thing at a time is a surprisingly powerful way to become more productive”. - Chris Baily, The Productivity Project
We have learned that dividing our focus among several different concerns slows us down and is the cause of many errors in programming.
I was watching a programming video the other day where the author built an if statement, and in the body of that conditional wrote a line that logged a message. Later, the author added an else and copied the log message to modify it. At that point, the author got distracted by another opportunity to improve the code.
When they tried to run the code, the log messages were still identical – the code reported exactly the same message whether the if condition passed or failed. It looked like the if condition didn’t work! The author was confused for a moment and returned to the code.
Once there, they realized that they’d forgotten to finish the one change they’d set out to implement.
Why did that happen? Because the author had a “while we’re at it moment” in the middle of making a change, forgot that they hadn’t completed the first change, and ended up running half-formed code.
I suspect that all the people who watched it had a nervous chuckle - nervous because that was a rookie mistake that we’ve all made. I remember seeing the author fall into “while we’re at it” mode and actually said out loud “no, no! Don’t get distracted!” I saw the mistake, I knew what would happen because I’ve been there plenty of times.
“An expert is someone who knows what all the mistakes look like” - Tim Ottinger
Save Your Game
We refer to committing code as “saving your game.”
This is an obvious parallel to video gaming. If you don’t save your game, and you run into trouble, you have no way to recover to a safe earlier time. You have to start over.
In software, we take tiny steps. Really tiny steps. We save often, when all the tests are running (“green”) and the code works. We commit because we may want to return to the recent past when everything was fine and all the tests passed. We can do that with a simple git reset –hard.
Joshua Kerievsky refers to the tactic of reverting to a save version as a Graceful Retreat.
It’s often faster to retreat and start over than it is to try to press on.
When people are working with several days’ worth of code uncommitted, and they run into some unforeseen problems, they’re stuck. They have to press forward. They can’t back up and regroup. If they were to reset, they could lose hundreds of changes.
Does that seem “normal” or “terrifying” to you? Because that sounds extremely unsafe to me.
Sure, you could probably untangle the unexpected problem with a few (more) hours in the debugger and by reading (a lot of) code. But what if you could back up to 5 minutes ago when everything was fine?
What do you mean you didn’t run all the tests and get a passing green bar 5 minutes ago? Surely you’ve ensured the code works at least a few times per hour, right? Isn’t that how everyone works?
Better Code Reviews
I spent a chunk of 2019 reviewing code in git repos. I learned an important lesson: the smaller and more focused a changeset, the shorter the time necessary to review it.
I could review 20 or 30 focused changesets in a bit less time than it took to review a single large changeset.
This is counter-intuitive. I know it sounds like doing 20 code reviews would be 20 times worse than reviewing one changeset, but that’s not how it works.
I’ve found this principle echoed by many technical leads and other code reviewers in various situations as well. When we are only examining one intention at a time, the review is easier and it’s quicker to spot and understand the technique being employed.
When I find people doing very large commits, or “squashing” all their intermediate commits together into one mega-commit, I flinch. I don’t want to do the code review on that bundle.
And I don’t want to do the merge either.
What He Said
Steven J Owns on Quora was answering a question: “What is a micro-commit?” Here is an abridged excerpt from his answer:
Most developers who start to use revision control go through a process of learning to commit more often.
This is a little trickier than it might sound because another standard practice with SCM is to only commit code that compiles cleanly with no errors and passes all the automated tests.
To resolve the tension between these two practices, they need to learn to modify their process.
They need to learn more discipline in tightly scoping their changes, biting off smaller chunks at a time, getting those chunks all the way to build and pass tests, and then committing them before touching a different part of the code.
This learning process can be painful but in the long run, makes a developer much more effective.
The more I learn about software development, the more I feel that scope control is fundamental to the process.
This echoed my own experiences in a deep way. I think that in the long run, a lot of becoming an effective developer is learning to take tiny steps and focus on doing one thing at a time.
Microcommits are consistent with this philosophy, both enabling and supporting good practice.
Why Rebase Works For Me
There is one other odd effect of micro-commits: rebase works great.
A lot of people on the internet have been telling me that they never do git pull –rebase because the merge always fails and leaves them in a weird (“headless”) state in their local codebase.
I always was confused to hear this because it doesn’t happen for me.
I learned that other people compose commits. They make a number of changes, and then choose which files and which changes to include in their commit changeset.
I never do. I have only done one thing since my last commit, and I usually commit all the files that changed. I don’t have some committed and some uncommitted changes. I don’t have unrelated changes in two or three different files.
Because I don’t have committed and non-committed or partially-committed files, git doesn’t have to work hard to rebase my change sets.
Also, because I integrate often (rarely a half-hour passes without integration) I’m never far from the version of code in the main code branch. It’s super-easy for me to do a rebase whenever I finish one of my tiny steps.
But We Are In A Hurry
I have great news for you.
We have found that the smaller our steps, the faster our progress.
I know that sounds counterintuitive and fishy. It doesn’t seem likely. But here we are.
There are a few things working in favor of the micro-committers:
- Merges are easy, so pulls are easy, and rebases are easy
- We can always gracefully retreat to a working version, so we can experiment and innovate.
- Our code reviews are quick (we often do them by group programming anyway)
- Our code always works, so we don’t need long debugging cycles
- Our focus is such that we don’t get confused and build things halfway, leaving silly bugs.
- We’re not afraid that a bad refactoring step will mess up days’ or weeks’ worth of code.
- We can pull and push more often, so we can share improvements with each other rapidly.
- We can go on breaks easily; we’re not going to lose our changes or our place.
Now, that doesn’t mean that micro-committing is some kind of panacea or miracle cure for all the problems you may have. Realize that our micro-committing has always been done in the context of TDD, and we embrace the practice of refactoring when our tests pass. These habits may contribute much to the success of our micro-committing.
If you don’t refactor, and you don’t do TDD, you don’t take disciplined breaks, you don’t do mob programming or pair programming, will micro-committing still help?
I don’t know. After several decades, I can’t even imagine working that way anymore. But maybe it will help you to know that there are people who work this way quite happily and effectively.
Come on in. The water’s fine.
If you are not working in a commercial team but a large open source project, your context is significantly different and you might want to try something different. See the macro-commit article written by Janek Bevendorff. You may still want to micro-commit so you can back out of any troublesome change, but maybe squash it down into one big patch for submission.