The Ultimate Guide to Writing Effective Commit Messages: Best Practices and Conventional Commits

The ultimate guide to conventional commits and writing nicer commit messages

The Ultimate Guide to Writing Effective Commit Messages: Best Practices and Conventional Commits

Want to watch this as a video? Click below 👇

Is writing good commit messages important?

The code in a commit explains what's happening, but the message explains why it's happening.

Commits are a powerful tool to improve the maintainability of projects and create documentation.

Good commit messages make it faster to review

Imagine this. Your deadline is in 2 hours, all you need to do is merge some code.

You submit your pull request.

Sample pull request with title "add code 2 make it work" and a comment that tags a team mate and asks them to review it right now
Pretend it's more than 1 file, please. Like 3 or 4 files? and ~100 extra lines of code. I didn't put much effort into this lol

BUT! Your stupid silly teammates are not reviewing it fast enough!!!

YOUR DEADLINE is in 2 HOURS!!!! if this isn't merged NOW you'll have to wait for the next release train, which means customers will be unhappy!!

Now, now. Slow down! You see, the thing is... it's hard to review pull requests when your team is busy and they don't even know what your pull request is.

If I see a pull request like this, my reaction is...

Ok, it adds code to make it work. What's it? And it's tested...? how? ok i love my team but i do not have time to review this and answer those questions, I will time-box it next week or something

You see, in my head, I only have so much brain capacity before I must scream. I have already planned out my day, and my week to meet my own deadlines.

Head empty

I'm not sure how long this would take for me to review, figure out how it's tested, and what it's meant to do. So, I must delay it until my Brain Capacity™️ frees up.

Peter Hutterer explains it better than I can:

Re-establishing the context of a piece of code is wasteful. We can’t avoid it completely, so our efforts should go to reducing it [as much] as possible. Commit messages can do exactly that and as a result, a commit message shows whether a developer is a good collaborator.

Commits are Git native, and GitHub pull requests are not

Ok so like we all learned git right? well, we probably learned it from GitHub or maybe Phabricator, or that weird Facebook one that uses stacked commits...

But here's a fun fact, GitHub pull requests, diffs, whatever you want to call them. They are not native to git.

🤓

Achtually there is git request-pull but when people say "pull request" they are almost always talking about GitHub-style pull requests. The Git version is email only and I have never seen anyone use this.

When you move from Phabricator to GitHub, the pull requests do not go with you.

You can write the nicest pull requests on earth, but if the commits are ugly it does not matter.

You may be thinking:

once a pull request is merged, why do I care?

Well, because...

Nicer commits let other programmers speed up the feedback loop

Picture this. I'm a developer being onboarded to a new project. I need to migrate some logs from A to B.

Ok, no problem. We've done this before. Let me look at my team's commits.

I see the files they've changed and I similarly change them.

Now time to push to a pull request.

Sample pull request comment from someone asking me to make manual SQL changes

Uh oh. There are manual steps??? Frantically I search documentation, Slack, and anything. I don't see this anywhere??

If the commit originally included all information related to the change like:

  • How it's been tested (step-by-step)
  • Any manual changes that have been made
  • What environment it's on, and what environment it will be on

I would not need to fight so hard to find out how to do this.

Git commits lets you use native Git tools

Imagine we find a bug in a production system. Ok, not good.

We know it landed in a commit at some point in the last 6 months.

Thankfully we can use Git Bisect which performs a binary search over commits to find the issue.

Can't really wrap my head around this one : r/ProgrammerHumor
enjoy your 1 meme

Now, here's a problem. We can identify the commit, but that's all we really know from bisect.

If the commit has a bad commit message we will not understand the context of the commit. Say the commit was made to fix a bug, obviously, we can't delete the commit.

But, if the commit did not contain useful information, we might. And we might break things even more.

Commits let us use Git native tooling which in turn improves our lives.

Other tools include:

  • Git Squash
  • Git Rebase
  • Git Merge
  • Git Blame
  • Git Revert
  • Git Log
  • Git Shortlog

and so on.

Ok, so git commits are important, Let's talk about how to write em starting with...

How to title a git commit message

From the git commit manpage:

Though not required, it’s a good idea to begin the commit message with a single short (less than 50 character) line summarizing the change, followed by a blank line and then a more thorough description.

The text up to the first blank line in a commit message is treated as the commit title, and that title is used throughout Git. For example, Git-format-patch(1) turns a commit into email, and it uses the title on the Subject line and the rest of the commit in the body.

it should:

  • Be concise
  • 72 characters or less (GitHub truncates this otherwise)
  • Describe exactly what the commit is doing

The first word of the title should be a verb, such as:

  • Fix -> Fixing a bug
  • Feat -> A new feature
  • Chore -> Often manual chore
  • CI -> Updating continuous integration
  • Docs -> Writing documentation
  • Tests -> Writing tests

This narrows down the scope even more so that instantly people know what you're trying to do. Like:

feat: add new api handler for burger-maker

or

CI: update CI image to 3.0.07

If your pull request is a breaking change, we can suffix the verb with an ! like feat!: change type def of /burger to Burger

🤔

A breaking change is a change which breaks how users expect our system to work and may cause bugs if they are not aware.

We can also scope down our title, even more, using parathesis like feat!(api): add handler for burger maker

Great commit title feat!(api): add handler for burger maker

Now when your team looks at the title, they know:

  • What type of change is it
  • Is it a breaking change or not?
  • What scopes does it affect?
  • What is it trying to do?

This method is called conventional commits

Conventional Commits
A specification for adding human and machine readable meaning to commit messages

And it's a nice convention we should follow for commits.

🤔

If your commit is atomic and quite small you can skip the rest of this article. Commits like fix spelling in API docs do not need any more information.

Body

Commits are documentation so we should write a good body.

🐬

Fun fact: If you use git commit without the -m it'll open the commit in the text editor your terminal is set to. The first line is the title, then a new line, then the rest is the body. GitHub will populate your pull request with the git commit body too, so you can get 2 birds with 1 stone.

How did you test it

and more, how can I test it?

Commits that say "I tested this" are not... helpful. Like how did you test it? Did you run the code and go "This works?" or test every edge case? Did you write automated tests, or is it manually tested?

Body says "tested and it works ok"

Python code is only executed as the interpreter reaches it. You could definitely have a bug in a function that isn't normally called and not notice it.

When you say "I tested this" it means I have to trust you.

👾

In an ideal world, our tests will be automated. In some areas, this is the kind of dream Sleeping Beauty would have.

I do trust you, but I would like not to. I'd like to read your commit and see you've tested it very well and I do not have to trust you.

Alsooo... I may work on your code base in the future. And I may need to test the code. And when I need to test it, I'll learn how to test it either through:

  • The documentation (if it exists)

or

  • How previous commits were tested

If neither of these things exists, it may take me a few hours to figure out how to test it, or worse! I may need to talk to you to figure out how to test it.

Engineering hours are wasted this way.

If we write how we tested things into the commit, we:

  • Remove trust from the equation
  • Se missed an edge case and someone calls it up
  • Future engineers do not need to spend time figuring out how to test

I'd like to see the commit structured like this:

Example pull request detailling a tests header with instructions on how to set up the testing environment along with curl commands that show exactly how to test the API endpoint

Where is this deployed?

🥑

You can skip this if your company has a simple dev / prod environment. Read on if you have multiple dev / prod environments

A small pet peeve of mine is when people say "This is deployed and tested".

ok, love that for you bestie.

But uhm... where is "deployed"?

If your tests involve calling an API endpoint or running the code in the environment, I want to be able to ssh into that environment and run it myself.

I don't want to, but I like the option. Especially if I spot an edge case that was missed.

And environments sometimes matter.

If your change involves a database change, was it tested in dev-59 which is fucked up by all measures and has been left to rot, or is it tested in dev-60 which still resembles our production environment.

Talking of production, some companies have multiple prod environments.

Where will this code be deployed to? Is it deployed to our FedRAMP environment which requires extreme scrutiny?

Or is it deployed to the prod environment for our internal tooling, which if it breaks only affects like 20 people?

Footers

Sometimes there is more info in a commit that we want people to know about, but we don't want to hide it in the body with a single sentence.

For example, what's more readable?

In this commit, we introduce a new feature for user authentication that enhances security and improves the user experience. We have implemented robust encryption and hashing techniques to securely store passwords. Additionally, we offer optional two-factor authentication (2FA) for added protection. The login and registration processes have been revamped for a smoother user experience. This commit has undergone manual review by Sadiyah in our London office on Thursday, our security expert.

Or this:

In this commit, we introduce a new feature for user authentication that enhances security and improves the user experience. We have implemented robust encryption and hashing techniques to securely store passwords. Additionally, we offer optional two-factor authentication (2FA) for added protection. The login and registration processes have been revamped for a smoother user experience.

Manually-reviewed-by: Sadiyah, our security expert, in the London office on Thursday.

Footers point out things in an extra obvious way.

It's not that I don't read your descriptions, it's just if I am on your team it's likely I know roughly what you're working on, so I might skip some descriptions if I feel like I know it already.

The "reviewed by Sadiyah" part is a bit buried, turning it into a footer makes it extra obvious for me.

We can include other footers, too.

If our pull request is related to a Slack channel, we can do slack-channel: #testing-rbac. If it relates to an incident, we can link the incident too.

Liked this article? Please subscribe to my newsletter or Youtube. It'll really help me out and it's free for you :) <3

Easy to copy template

Commit messages are documentation, and investing the time now to write better commits will pay us dividends in the future.

Sign up to my email list below to see the exact template on this page:

  • Makes my co-workers love me
  • Reduce my workload by not having to explain my code or tests

And as a bonus, you'll also learn...

  • How to apply this automatically in Git

You can follow this blog post to set the template as default:

How to specify a git commit message template for a repository in a file at a relative path to the repository?
Is there a way to specify a git commit.template that is relative to a repository? For configuration an example is $ git config commit.template $HOME/.gitmessage.txt But I would like to specify a

Here's the template:

feat!(API) change type definition of burger

# Testing
I tested this by...

# Deployed
This is deployed to...

reviewed-by: sadiyah
slack-channel: #test-123