The Ultimate Guide to Writing Effective Commit Messages: Best Practices and Conventional Commits
Tags: programming
Published: 2023-06-24 13:01:46

---
title: The Ultimate Guide to Writing Effective Commit Messages: Best Practices and Conventional Commits
published: true
date: 2023-06-18 07:48:46 UTC
tags: programming
canonical_url: https://skerritt.blog/writing-better-commit-messages/
---

 ![The Ultimate Guide to Writing Effective Commit Messages: Best Practices and Conventional Commits](https://skerritt.blog/content/images/2023/06/git_commit_2x.png)

**Want to watch this as a video? Click below 👇**
{% youtube uqJ1BgLfFsU %}


# 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.

![The Ultimate Guide to Writing Effective Commit Messages: Best Practices and Conventional Commits](https://skerritt.blog/content/images/2023/06/Screenshot-2023-06-13-at-19.35.55.png)
_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.

![The Ultimate Guide to Writing Effective Commit Messages: Best Practices and Conventional Commits](https://i0.wp.com/www.michigandaily.com/wp-content/uploads/2023/03/Cassidy-Brimer-1_Head-empty-1.png?fit=1200%2C907&ssl=1)

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](http://who-t.blogspot.com/2009/12/on-commit-messages.html?ref=skerritt.blog)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](http://www.osnews.com/story/19266/WTFs_m?ref=skerritt.blog) [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.**

Actually 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](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project?ref=skerritt.blog) 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.

![The Ultimate Guide to Writing Effective Commit Messages: Best Practices and Conventional Commits](https://skerritt.blog/content/images/2023/06/Screenshot-2023-06-13-at-19.46.37.png)

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](https://git-scm.com/docs/git-bisect?ref=skerritt.blog) which performs a binary search over commits to find the issue.

![The Ultimate Guide to Writing Effective Commit Messages: Best Practices and Conventional Commits](https://i.redd.it/g7r4mnsok78z.jpg)
_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](https://www.kernel.org/pub/software/scm/git/docs/git-commit.html?ref=skerritt.blog#_discussion):

> 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:

![The Ultimate Guide to Writing Effective Commit Messages: Best Practices and Conventional Commits](https://skerritt.blog/content/images/2023/06/Screenshot-2023-06-14-at-07.39.01.png)
_feat: add new api handler for burger-maker_

or

![The Ultimate Guide to Writing Effective Commit Messages: Best Practices and Conventional Commits](https://skerritt.blog/content/images/2023/06/Screenshot-2023-06-14-at-07.39.34.png)
_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`

![The Ultimate Guide to Writing Effective Commit Messages: Best Practices and Conventional Commits](https://skerritt.blog/content/images/2023/06/Screenshot-2023-06-14-at-07.40.06.png)

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_

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

If your commit is [atomic](https://www.freshconsulting.com/insights/blog/atomic-commits/?ref=skerritt.blog) 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?

![The Ultimate Guide to Writing Effective Commit Messages: Best Practices and Conventional Commits](https://skerritt.blog/content/images/2023/06/Screenshot-2023-06-14-at-07.40.54.png)

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:

![The Ultimate Guide to Writing Effective Commit Messages: Best Practices and Conventional Commits](https://skerritt.blog/content/images/2023/06/Screenshot-2023-06-14-at-07.45.22.png)

## 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](https://www.fedramp.gov/?ref=skerritt.blog) 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?

![The Ultimate Guide to Writing Effective Commit Messages: Best Practices and Conventional Commits](https://skerritt.blog/content/images/2023/06/Screenshot-2023-06-14-at-08.10.02.png)

# 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