Skip to main content

Command Palette

Search for a command to run...

Writing Better Git History with Conventional Commits

Updated
9 min read
Writing Better Git History with Conventional Commits
G
Glory Praise Emmanuel is a software engineer who builds full-stack software solutions and systems. She writes about software engineering, Web3, AI, and career growth in tech. She is passionate about open source, developer education, and community building.

Working with Git and GitHub feels like magic the first time everything clicks. You run a few commands, push to a remote, and suddenly your code is safely living “in the cloud.” Git doesn’t complain about your commit messages either, hence you can write update, or even another commit and smash the keyboard, and it will happily accept it.

That freedom is fun at the beginning. But the moment you start collaborating with a team or just try to debug something in your own project, a few weeks later, you realise the downside: your commit history reads like noise. You can’t quickly tell what changed, why it changed, or which commits are safe to revert.

Since Git doesn’t give you a built‑in standard for commit messages, developers created one: Conventional Commits. It’s a lightweight convention that adds just enough structure - things like feat, fix, and chore - so your commit history becomes easier to read, understand, and track.

In this article, we’ll walk through what Conventional Commits are, why they’re worth using, and how to start applying them today, along with some simple Git commit message best practices.

Why commit messages matter (more than you think)

Picture this: you open git log on a project you haven’t touched in months and see messages like:

  • update

  • fix bugs

  • changes

  • final final fix

Technically, Git is fine with this. But you’re not. You’re trying to answer questions like:

  • When did we introduce this bug?

  • Which commit added this feature?

  • Is it safe to revert this change?

Good commit messages turn your history into a searchable journal of your project, not a mystery novel. They:

  • Help you understand what changed and why without reading every difference.

  • Make debugging easier when you’re tracking down a regression.

  • Improve code reviews by showing the story of a feature step by step.

  • Enable automation, like generating changelogs or semantic versioning from commits.

So the real question isn’t “Why bother writing good commit messages?” It’s “Why make life harder for yourself and your team later?”

What makes a “good” commit message?

Before Conventional Commits enters the chat, let’s cover the basics. A good Git commit message usually follows a few simple guidelines:

  • Keep the subject line short (around 50 characters or less).

  • Capitalize the first word; don’t end the subject line with a period.

  • Use the imperative mood in the subject: “Add login page,” not “Added login page” or “Adding login page.”

  • Separate the subject from the body with a blank line.

  • Wrap the body at about 72 characters per line so it’s readable in terminals.

  • Use the body to explain what changed and why, not every line of how.

A classic example looks like this:

Add validation for user email

Reject invalid email formats at the API layer to avoid database errors and improve error messages for clients. 

You could stop here and already be ahead of many repos. But Conventional Commits gives you a simple structure on top of this that makes your history even more powerful.

Conventional Commits: structure on top of Git

Conventional Commits is a specification that proposes adding a tiny bit of structure to your Git commit messages. It doesn’t change how Git works; it just gives you a standard format that humans and tools can understand.

The basic format is:

(optional-scope): short description 
  • type: what kind of change is this?

  • scope (optional): which part of the codebase it touches.

  • description: a short, imperative summary of the change.

This gives you commit messages that are both readable and machine‑friendly.

Common Conventional Commit types

Here are some of the most common type values and what they mean:

  • feat: a new feature.

  • fix: a bug fix.

  • chore: maintenance, tooling, dependency updates, etc.

  • docs: documentation changes only (README, guides, comments).

  • style: formatting-only changes (whitespace, semicolons, etc.).

  • refactor: code changes that aren’t features or bug fixes.

  • test: adding or updating tests.

  • perf: performance improvements.

  • ci: changes to continuous integration configuration.

  • build: changes to build tools or external dependencies.

  • revert: reverting a previous commit.

These types act as labels for your history. When you scan git log, you can immediately see what each commit represents.

Optional scope: zooming in

The optional scope gives more context about which part of your project changed.

Examples:

feat(auth): add email login

fix(cart): handle empty cart state

docs(api): document rate limiting

Now your log doesn’t just say “feat” or “fix”—it tells you where that feature or fix lives.

A day in the life with Conventional Commits

Let’s say you’re building a small web app with authentication and a profile page. A focused morning of work might produce commits like these:

feat(auth): add email login

Allow users to create accounts and sign in using their email and password. Store password hashes using bcrypt.

fix(auth): handle invalid token error

Return a 401 instead of 500 when the auth token is invalid so clients can redirect users to the login page.

docs(auth): update login section in README

Add steps explaining how to configure environment variables for local authentication.

feat(profile): allow avatar upload

Let users upload profile pictures from the settings page. Store images in S3 and serve them via CDN. 

Reading this a month later, you don’t need to open a single file to understand what happened and in what order. The commit history is telling you a story.

Handling breaking changes

Sometimes a change is more than just a feature or fix; it breaks something for users. Conventional Commits has a built-in way to signal breaking changes.

You can:

Add a ! after the type or scope, or

Include a BREAKING CHANGE: footer in the body.

For example:

feat!: remove deprecated auth endpoints

BREAKING CHANGE: Legacy /v1 auth endpoints have been removed. Clients must use the /v2 endpoints with token-based auth. This is especially useful if you hook your commits into tools that automatically manage semantic versioning or generate changelogs.

Conventional Commits + best practices = clean history

Conventional Commits doesn’t replace general Git best practices; it builds on them. You still want:

  • A short, imperative subject line, no trailing period.

  • A blank line before the body.

  • A wrapped body that explains what and why.

Putting it all together, your commits might look like this:

feat(profile): add avatar upload

Allow users to upload profile pictures from the settings page. Images are stored in S3 and served from a CDN.

fix(profile): prevent crash on missing avatar

Return a default avatar URL when the profile has no image instead of causing a null reference error.

chore: bump eslint and prettier

Update eslint to v9 and prettier to v3 to keep tooling current and align with team formatting rules.

docs: add contribution guide

Document how to clone the repo, run tests, and follow the commit message convention for new contributors. This is readable for humans, and predictable enough for tools to parse.

Git commit message best practices (checklist)

Here’s a simple checklist you can follow for every commit, with or without Conventional Commits:

  1. Always write a real message
    Avoid update or empty messages. Describe the essence of the change.

  2. Use a clear, short subject line
    Aim for 50 characters or less so it doesn’t get cut off in tools and terminals.

  3. Write the subject in imperative mood

    • Bad: Fixed login bug

    • Better: Fix login bug
      This reads well with “If applied, this commit will…”.

  4. Capitalize correctly, avoid punctuation at the end
    Think of it like a headline: “Add API pagination,” not “add api pagination.”

  5. Separate subject and body with a blank line
    Many Git tools expect this and only show the first line as the summary.

  6. Wrap the body around 72 characters per line
    This makes your message easy to read in terminals and in git log.

  7. Explain the what and why in the body
    Don’t assume your code is self‑explanatory. Explain what changed and why; the diff will show how.

  8. Make your commits focused
    Try to keep “one logical change per commit” so it’s easy to revert or review later.

When you layer Conventional Commits on top of these practices, your history becomes structured, searchable, and easy to navigate for everyone.

“But there’s no official standard…” and why that’s fine

Here’s the important thing: Git itself doesn’t enforce any of this. There is no official, global standard you’re required to follow. You can technically keep committing with messages like stuff forever.

Conventional Commits is just a community-driven convention that many teams and open source projects have adopted because it works well. That flexibility is actually a strength:

  • You can adopt Conventional Commits as-is.

  • You can tweak the type list for your stack or team.

  • You can start small - maybe just feat, fix, chore and grow over time.

The real win comes when your project picks a convention, writes it down, and uses it consistently.

A simple section in your README or CONTRIBUTING.md might say:

## Commit message convention

We use Conventional Commits for all commits:

(optional-scope): short description

Common types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert.

- Use lowercase types.
- Write the description in imperative mood.
- Add a body when you need to explain the what and why.

From there, you can optionally add a commit lint tool or a Git hook to enforce the convention, but that’s an optimization, not a requirement.

Conclusion

The best time to start writing good commit messages was when you created your first Git repo. The second-best time is your next commit.

You don’t need to overthink it. Just pause before you write your next commit message and ask yourself:

  • What type of change is this (feat, fix, chore, docs, …)?

  • Which part of the project did I touch (auth, API, UI, …)?

  • How can I describe this in a short, clear sentence?

Do this over and over, and your commit history turns from noise into a readable story of your project. Your teammates will appreciate it. And honestly, your future self - six months from now, debugging a weird bug - will absolutely thank you for taking the time to write clear, conventional commit messages today.

Keep building.

More from this blog

G

Glory Praise Emmanuel's Blog

52 posts

I write about software engineering, Web3, AI, and career growth in tech.