About the importance of the way you check your code in version control

Милан Тодоров
4 min readJun 15, 2023

--

Universities, academies, even employers underestimate how important it is to commit your code in a certain way. It seems all they care about is:

  • Universities — algorithms are king
  • Academies — frameworks are king
  • Employers — spew new code as much and as fast as possible

Let me talk to you about one of the things nobody really teaches you — how to commit your code in a good way.

Why is that even important you might say — it is really important for minimising communication needs among team members and also the need for documentation. Imagine if you could use all the time you lost in drawing diagrams or answering questions on code reviews for other, often more rewarding things, like discussing the bigger picture and the design, or gaining some new skill. This brings me to possible indications that you need to work on the way you commit your code:

  • you often get asked to draw diagrams so others can understand what you did
  • you often get asked questions like “can you summarize shortly what you did, why and how”

Here is what to do:

  1. Write meaningful, specific commit messages. Avoid “fix”, “a good day’s work”, “regular commit” and similar vaguenesses as the sole content of your commit message. Include some reference (ticket/issue number) that might help understand the requirement. Describe precisely what you are doing with this commit — “#65 refactor: move method #methodName from ClassOne to ClassTwo” is an example of what a good commit message looks like.
  2. Commit small amouts of changes. This is critical for your teammates ability to understand what you do — simply because it is difficult to focus on a 100-line diff. I even believe that committing large portions of code at once is the reason for bad commit messages — you’ve done so much and now you have trouble describing what you did, so you resort to more vague messages described in the previous point. One possible exception to this rule is when you rename a method which is used a lot, so that makes a really long diff. But hey, it is just a simple change, even if it is big, so no real congnitive overload will take place at your teammates’ brains.
  3. Never, ever squash automatically when you merge your changes. This leads to big diffs 99% of the time. In my opinion, squashes and fixups should be done only manually and with extra caution. You want to squash only things that you didn’t get right the first time so you needed to change it in a later commit. You want to make it look like you got it right the first time. Example: it is OK to fixup these two commits together:
    “introduce a new member constant”, “change constant to some other value”. Some sidenote on pt. 3: One of the seniors at my current job shouted once “who doesn’t squash everything when they merge”. I raised my hand immediately. Him: “Why wouldn’t you do it, you should absolutely do it every time”. Me: “In my experience it leads to big diffs that are close to impossible to comprehend”. Him: “Well that’s because you don’t focus on refining your agile stories enough, they have to be small enough”. Me: “The commit granularity I like is really small changes, so if I try to refine my stories that much, I will end up 90% of my time refining stories”. Him: “And what about when you change a variable name or value several times, you don’t want to see 5 commits about that”? Me: “True, I always squash those, but only them, manually”. Him: “And what about if you want to revert the whole thing together”? Me: “Then you have merge commits which can be used to group several commits together and also for reverting all of them”. This brings me to the next point.
  4. Never, ever use fast-forward as your merge strategy. If you do this, you lose the ability to group the commits using a merge commit. For git, first rebaseon top of the target branch, then use --no-ff when you merge manually. For automatic merges, gitlab’s “Merge commit with semi-linear history” does a pretty good job. Note: the rebaseis optional, but strongly recommended for simplicity. Supposing 2 features developed in parallel and if you skip it, the history will look like left of the pic. The merge commit 5b5428da being a 3-way commit will have 2 diffs, to each of its parent merges 131b01d0 and b390af25. When you do the rebase the history is greatly simplified and 5b5428da has only one parent merge —131b01d0.
Merging without (left) and with (right) a preceding rebase

5. When you end up with a big pile of uncommitted changes (you will inevitably) even when you try to be hardcore about rules 1–4, stage changes interactively to create several smaller commits.

It is important to use version control as a weapon for communication and documentation as much as possible. Do the steps above and see how the time for documentation, reviews and calls reduces immensely.

--

--

No responses yet