DEV Community

Cover image for Stop Writing Bad Commit Messages: Use Conventional Commits Instead
Richa Parekh
Richa Parekh

Posted on

Stop Writing Bad Commit Messages: Use Conventional Commits Instead

You've probably seen some of these commit messages if you've been coding for a while:

  • final changes
  • bug fixed
  • new update

Even I used to do this. 😬
These may not seem like a big deal at first. After all, the code works, right? However, these unclear commit messages become a source of confusion and frustration when you review your Git history three months later, or even worse, when a teammate tries to figure out what went wrong.

Poor commit messages cause many issues:

  • Debugging is more difficult because it takes longer to understand why a change was made.
  • A messy project history makes collaboration difficult because everything looks random.
  • Automation opportunities are lost because free-form messages cannot be parsed by tools.

That’s where Conventional Commits come in. This community-driven, lightweight standard gives commit messages structure without sacrificing their human-friendliness. Adopting this convention can immediately enhance your workflow, regardless of whether you're working in a team, coding alone, or contributing to open source.

What is Conventional Commit?

In simple words, it is a Rulebook for writing commit messages in git.

<type>(<scope>): <short description>
Enter fullscreen mode Exit fullscreen mode

The open-source community, particularly from projects like Angular and the larger JavaScript ecosystem, contributed to developing this standard. The goal? to eliminate uncertainty, enable automation such as changelog generation, and make commit history self-explanatory.

There are no hard and fast rules, and you don't need any particular tools to get started. It's simply a naming convention that provides your commits with structure and meaning.

Format & Syntax

<type>(<scope>): <description>

[optional body]

[optional footer(s)]
Enter fullscreen mode Exit fullscreen mode

1. Type (Mandatory)

  • The type tells you what kind of change was made. Examples: feat, fix, docs.

2. Scope (Optional but Useful)

  • The scope is the part of the codebase affected. For example:
  1. feat(auth): add Google login
  2. fix(cart): resolve item quantity bug
  • If your project is small, you might skip the scope. For larger codebases, it’s a lifesaver.

3. Description (Mandatory)

  • A short summary in the present tense.

feat(ui): add dark mode toggle
added dark mode toggle

4. Body (Optional)

  • Here’s where you explain why the change was made or add extra details.
fix(api): handle null values in user response

Previously, the API would crash if the user object was null. 
Now it safely returns an empty object instead.
Enter fullscreen mode Exit fullscreen mode

5. Footer (Optional, but for metadata)

  • Footers are used for breaking changes or referencing issues:
BREAKING CHANGE: The API no longer supports v1 endpoints.
Enter fullscreen mode Exit fullscreen mode

or

Closes #123
Enter fullscreen mode Exit fullscreen mode

Examples for Each Type

feat (A new feature)

feat(ui): add dark mode toggle

Implemented a dark mode toggle in the settings page.
- Added Tailwind dark mode classes
- Saved preference in local storage

Closes: #14
Enter fullscreen mode Exit fullscreen mode

🐞 fix (A bug fix)

fix(auth): prevent login with expired token

Fixed an issue where expired JWT tokens were still accepted.
Updated middleware to validate expiry timestamp.
Enter fullscreen mode Exit fullscreen mode

📖 docs (Documentation changes only)

docs(readme): add contribution guidelines

Included steps for submitting pull requests and reporting issues.
Enter fullscreen mode Exit fullscreen mode

🎨 style (Code style/formatting (no logic change))

style(css): format homepage layout

Applied Prettier formatting and adjusted spacing in hero section.
No functional changes.
Enter fullscreen mode Exit fullscreen mode

🔄 refactor (Code changes that don’t add features or fix bugs)

refactor(api): simplify user creation logic

Removed duplicate validation checks and consolidated error handling.
Enter fullscreen mode Exit fullscreen mode

perf (Performance improvement)

perf(db): optimize order query

Replaced N+1 query with a single JOIN to improve load times.
Enter fullscreen mode Exit fullscreen mode

🧪 test (Add/modify tests)

test(cart): add unit tests for checkout flow

Covered cases:
- Empty cart
- Multiple items
- Discount codes
Enter fullscreen mode Exit fullscreen mode

🛠️ chore (Maintenance, tools, dependencies)

chore(deps): update Tailwind to v3.5.2
Enter fullscreen mode Exit fullscreen mode

Now let's understand this with a simple real-world use case:

Imagine you’re building a Chat App.

  1. You add a login feature
    feat(auth): add email/password login

  2. You fix a bug where messages aren’t loading
    fix(chat): resolve empty message list bug

  3. You update the README with setup steps
    docs(readme): add local setup guide

  4. You refactor the message component for cleaner code
    refactor(ui): simplify message bubble component

Now you are able to quickly identify what changed and why when you (or your team) review Git history.
A release tool may automatically create a CHANGELOG.md file with the following sections if you run it:

## Features
- add email/password login (auth)

## Fixes
- resolve empty message list bug (chat)

## Docs
- add local setup guide (readme)
Enter fullscreen mode Exit fullscreen mode

This is the real magic 🪄, turning commit messages into living documentation.

Why It Matters

For Individuals
Clarity for future you: You'll know exactly what each commit does when you go back to a project weeks or months later.
Professionalism: Your GitHub profile will stand out to future employers and collaborators if you have a clean commit history.
Personal growth: As a developer, you can improve your thinking skills by learning to accurately describe your changes.

For Teams
Smooth collaboration: Everyone is able to quickly figure out what other people are working on.
Automation: Tools like commitlint and semantic-release can parse commits to generate changelogs, bump versions, or trigger CI/CD workflows.
Reduced friction: Spend more time creating features and less time dealing with unclear commits.

Conclusion

Commit messages are the story of your project; they are not just a side note. A messy history makes it more difficult to maintain your work and makes it more difficult for others to understand it. Conventional Commits solve this with a straightforward, easily readable standard that scales well.

Adopting Conventional Commits is one of the simplest ways to improve your workflow, whether you're working on a side project, freelancing, or contributing to open source.

Although it might seem like a small change, but it develops a clear, strong history that tells the story of your project.

Have you previously tried Conventional Commits? Or do you want to use them in your upcoming project? I'd love to know how it works for you, so please share your experience in the comments section!

Top comments (0)