1. Is Your Commit History Running Around Naked?
Let's start with a scene you've probably seen (or created yourself). You open a project's Git history, and this is what greets you:
* asdasd
* fixed a bug
* update
* added some new stuff
* changes
* ok this time it works
Can you tell what actually happened here? What bug was fixed? What "new stuff" was added? Which commit introduced that feature the client is asking about? Nobody knows. Not you, not your teammates, and definitely not the "future you" who has to dig through this mess six months from now.
This kind of commit history creates real problems:
- You can't quickly locate issues. When something breaks, you have no idea which commit is the culprit.
- You can't automate anything. Tools can't generate a changelog from "asdasd."
- Team collaboration suffers. Your teammates have to open every commit to understand what changed.
The good news? There's a simple, widely-adopted solution called Conventional Commits. In this article, we'll walk through what it is, its format, and — most importantly — plenty of real-world examples showing exactly which prefix to use in which situation. By the end, you'll be ready to start using it right away.
2. What Are Conventional Commits?
2.1 The One-Sentence Explanation
Conventional Commits is a lightweight convention that gives your commit messages a set of rules. In plain terms: it makes every commit both readable and predictable, so instead of vague notes, each message clearly states what kind of change it is.
It's not a complicated framework or a heavy tool — it's just an agreement on how to write commit messages.
2.2 Why Bother?
You might be thinking, "It's just a commit message, does it really matter?" Here's why it's worth it:
- Instant clarity. With a type prefix, anyone can glance at the history and immediately understand what each commit did — whether it added a feature, fixed a bug, or updated docs.
- Automatic changelogs. Because the format is structured, tools can automatically generate a
CHANGELOGfor you. No more manually writing release notes. - Automatic version bumps. Conventional Commits works hand-in-hand with Semantic Versioning (SemVer). By describing features (
feat), fixes (fix), and breaking changes (BREAKING CHANGE), tools can automatically decide whether to bump a major, minor, or patch version.
Here's a helpful analogy: think about how you organize your email with subject labels like "Work," "Personal," or "Urgent." Those labels help you find and sort things at a glance. Conventional Commits does the same thing for your code history — it gives each change a clear category tag.
3. The Basic Format
3.1 What the Standard Format Looks Like
A conventional commit message follows this structure:
<type>[optional scope]: <description>
[optional body]
[optional footer]
Don't be intimidated — most of the time you'll only use the first line.
3.2 Breaking It Down Piece by Piece
- Type (required): A single word describing the nature of the change, like
featorfix. It's followed by a colon and a space. - Scope (optional): The specific module or area affected by the change, wrapped in parentheses right after the type. For example,
feat(login)means the feature relates to the login area. - Description (required): A short summary of what you did, placed immediately after the colon and space.
3.3 A Complete Example, Decoded
Let's take this commit message:
feat(login): add SMS verification code login
Here's what each part means:
feat→ the type: this is a new feature(login)→ the scope: it affects the login module:→ separator (colon + space)add SMS verification code login→ the description: what was actually done
See? Once you know the pattern, it reads almost like a sentence.
4. The Most Common Commit Types + Real Scenarios
This is the heart of the article. Instead of memorizing definitions, let's approach it as "here's the situation → here's the prefix you use."
4.1 The Two You'll Use Most: feat and fix
feat — Adding a New Feature
When to use it: You wrote brand-new functionality that didn't exist before.
- You added a "user login" feature to your website →
feat: add user login feature - You added a "clear cart" button to the shopping cart →
feat(cart): add one-click clear cart button
fix — Fixing a Bug
When to use it: You solved an existing problem or error.
- The checkout button did nothing when clicked, and you fixed it →
fix(cart): fix checkout button not responding - The login page looked broken on mobile, and you corrected the layout →
fix(login): fix mobile login page layout issue
💡 Tip:
featandfixcover about 99% of your day-to-day work. If you only remember these two, you're already ahead of most people.
4.2 The Types People Often Confuse
docs — Documentation Only
When to use it: You only changed documentation — the README, code comments, guides — and touched no actual logic.
- You added installation instructions to your project →
docs: add project installation steps
Clarification: Even adding a comment inside your code that explains a piece of logic counts as docs, as long as you didn't change the logic itself.
style — Pure Formatting Changes
When to use it: You adjusted indentation, spacing, or semicolons — the logic is completely unchanged, it's purely cosmetic.
- You ran Prettier across the whole file →
style: unify code indentation
Clarification: The key difference from refactor is that style involves no logic changes whatsoever, just appearance.
refactor — Code Restructuring
When to use it: You changed the structure or the way code is written, but the behavior is exactly the same as before — it's neither a new feature nor a bug fix.
- You extracted three repeated blocks of code into a single shared function →
refactor: extract duplicate form validation into shared function
Clarification: A simple test — if the change is something a user would never notice, and it's not just a formatting tweak, then it's a refactor.
test — Test-Related Changes
When to use it: You added or modified test cases.
- You added unit tests for the login API →
test: add unit tests for login API
chore — Miscellaneous / Tooling Work
When to use it: The "housekeeping" tasks that don't fit any of the above, like upgrading dependencies or tweaking config files.
- You upgraded the lodash version in your project →
chore: upgrade lodash to latest version
4.3 Advanced Types (Good to Know, Use As Needed)
| Type | When to Use It | Example |
|---|---|---|
build |
Changes to the build system | build: update webpack bundling config |
ci |
Changes to CI/CD pipeline config | ci: update GitHub Actions workflow |
perf |
Performance optimizations | perf: optimize homepage image loading speed |
4.4 Quick Reference: Which Prefix Should I Use?
When you're not sure, walk through this decision checklist top to bottom:
Did you add a new feature? → yes → feat
Did you fix a bug? → yes → fix
Did you only change documentation? → yes → docs
Only formatting (no logic change)? → yes → style
Restructured code (same behavior)? → yes → refactor
Is it test code? → yes → test
Dependencies / config / misc? → yes → chore
Stop at the first "yes" — that's your prefix.
5. Advanced: Marking Breaking Changes (Optional)
5.1 What Is a "Breaking Change"?
A breaking change is any modification that makes the old way of using your code stop working — in other words, it's not backward-compatible.
5.2 Two Ways to Mark It
- Method 1: Add
BREAKING CHANGE:in the footer, followed by a description of what changed. - Method 2: Add an exclamation mark
!after the type (or scope), likefeat(api)!: change user endpoint response format.
Example scenario: Suppose you renamed a field in an API response from userName to username. Any old code calling that field will now break. That's exactly when you should mark it as a breaking change, so anyone reading the history immediately knows to update their code.
6. Making the Rules Enforce Themselves: Recommended Tools
Relying on willpower alone is risky — sooner or later someone (maybe even you) forgets and types "fixed stuff." That's where tools come in.
- commitlint: Automatically checks whether your commit message follows the convention.
- husky: Hooks into the
git commitprocess, triggering the check at the moment you commit. If the message doesn't follow the rules, the commit is blocked outright.
Used together, these two tools mean everyone on the team is effectively required to follow the convention — no relying on memory or good intentions. Non-compliant messages simply can't get through.
7. Start Writing Commits That Actually Mean Something
Let's wrap up. Here's what to take away:
- Remember the two essentials.
featandfixcover the vast majority of your commits. Get comfortable with those first, and pick up the rest as you go. - This isn't busywork. A clean, structured commit history isn't "formality for formality's sake" — it's a gift to the future you and to every teammate who ever has to read your history.
- Try it right now. The next time you commit code, write one conventional commit. Watch how much clearer your history looks, and you'll never want to go back.
And if you want to go deeper, the official site at conventionalcommits.org has the full specification in multiple languages. But honestly? You already know enough to start today.



