Git Conventional Commits: From "Random Commit Messages" to Professional Development

July 21, 2026
23 min read
Modern illustration of structured Git commits, with abstract shapes representing organized branches and version control flow.

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 CHANGELOG for 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 feat or fix. 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: feat and fix cover 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), like feat(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 commit process, 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. feat and fix cover 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.

Comments

Log in to post a comment

Related articles