I counted the distribution of my commits by type and the result was enlightening!
Before starting the numbers, let’s get to the same page:
Git
You’re using Git, right? And, do I need to even explain what it is?
Also, if you’re using some more modern VCS (version control system), then you probably have a good reason. But if you’re still using some older tool… changing shouldn’t be that hard (I know because I had to convert multiple SVN repos to Git some years ago and never looked back).
However… if you’re not using any or you think you’re using them because you’re leaving things in Dropbox… are you ok? Do you need to talk?
Atomic Commits
Committing became a routine more complex than just doing a bunch of stuff and adding a “small changes” message.
I carefully check everything I did, splitting it into smaller commits, naming and describing what's happening with care. It’s to the point some commits have only one line and I don’t shy away from committing line by line from a file if that’s what it takes, not only that, I had multiple commits where the changes were smaller than the message.
This pays out when you are able to navigate commit by commit with the messages as a commentary of what and sometimes why you did that.
Conventional Commits
This is a specification on how to commit something and you probably saw it in the wild or in big projects. It usually goes with “something”(optional scope): message (that has a header but can also include a body and footer). The “something” there, among others, is usually: feat (feature), fix, refactor, or test.
First, how about you?
What do you think you would see if you counted yours?
The reason why I went counting was that I had a hunch that more than half of my commits were refactoring because I refactor stuff a lot.
I don’t believe in having perfect code from the get-go, but even a little refactoring can go a long way to make something a lot more pleasant to read and maintain and would like to show that to colleagues that might not care as much about the code as I do.
The count!
Type | Count | Percentage |
---|---|---|
fix | 104 | 7% |
feat | 568 | 38% |
refactor | 322 | 22% |
test | 291 | 19% |
OTHER | 212 | 14% |
TOTAL | 1497 | 100% |
These are the numbers I got after summing commits from the front and backend projects I’m working on right now. On the “other” there are things like docs, chore, build, and others with only a couple of commits.
Between the back and front, there were some differences, but I know that I usually already commit a feat of “use cases” along with a lot of tests because the tests help me shape the “use cases” and I refactor a whole lot more in the frontend because there’s a lot of components and I move them all the time. Even then, it was just a few percentage points, and overall the distribution is pretty much that.
After the count
Unfortunately, there’s no way, or at least it would be a hard endeavor to actually calculate the time spent on those. I do still feel I refactor half of the time. Then again, I usually never commit the first version even in a “feat” and while doing “tests” I usually refactor more.
But after putting it into numbers, I feel it’s a good distribution. I manage to add a lot of “feats” with only a small amount of “fix” and I do feel I’m moving fast even though almost half of the commits I’m “refactoring”, “testing” and adding “docs”.
It’s as Uncle Bob said, “The only way to go fast is to go well”.
Tell me your counts and what you think about it
If you already do atomic commits and use conventional commits, then let’s compare numbers and perceptions!
If you don’t, no worries, start today, and instead of mindless committing, take time to give a second read of your code as you commit each small piece and catch some errors before anyone else has to point them to you.
Top comments (5)
I don't do atomic commits and I disagree with the emphasis placed on the practice. As software developers, our focus is to build and ship products, not over-analyze micro-practices like commits.
Atomic commits are irrelevant to some of us in the industry because we leverage squash on merge, which condenses all the work done in a feature or hotfix branch into a single commit on the main trunk. This keeps the main trunk leaner, and also reduces the headaches of reversing single commits that are actually dependent on the commit order to properly function.
There's one point you might have the wrong idea.
At least what I do is not about over analysing commits, but the code before I commit it. It's just as much care as with naming functions, variables and writing comments.
In your case of squashing on merge, I can still see it as "bigger" commits.
Do you do big refactors along new features inside one merge or do you split in a feature merge and a refactor merge? And when you find a bug, do you mix the solution with other stuff or merge just the fix?
I know for experience that mixing too much in one single merge is very taxing for reviewing, if you give me a refactor only merge I expect a lot of changes that don't actually change what the code does. Meanwhile a feature one might have or not lots of changes, but they should be focused on one thing. Same for a fix one.
Not sure if there's a "conventional merges", but depending on how you're naming the merges, you can still find out something!
Respectfully, I don't have the wrong idea, we'll have to agree to disagree here.
Your response is difficult to understand, but I'll try to respond to your questions.
We don't operate in the context you're describing. Big refactors? Not a thing. We have a product team which scopes the work assignments for both feature development and bug fixes, and engineers review these assignments before accepting them to ensure the scope is feasible. Each work assignment is performed in isolation of its own branch and merged using a pull request. Nothing is ever committed directly to the trunk (main/master branch).
I get it, you work in branches and squash before merge. What I'm saying is that it doesn't matter. You still do features, fixes and refactors.
I'm not saying "big refactors", I'm saying from moving stuff around to habitual cleanups. I might relentlessly refactor stuff, but anyone need some refactoring here and there. And then there come a PR that is just too big and doing too much at one time.
We know people won't read a big PR, it's not really feasible and that's why it's better to do small and focused ones: refactors, feature, fixes...
One of the things I love about conventional commits is it lets you quickly see the type of commit without seeing the code change. Like your article mentioned, this can help us gauge whether we're investing enough in refactoring, testing, etc.
In my team, adopting this convention has made our repo much easier to navigate. It also helped us break down our changes into small increments => make our PRs smaller => speed our reviews => deploy more often.