Skip to main content
← blog

A Practical Guide to Commit Conventions

git best-practices tutorial

On any project, keeping a clear and readable history of changes over time is essential. One of the tools that makes that possible is Git, where every change is recorded as a “commit”. The semantic commit is a convention that aims to make those records clearer and more useful, keeping every message in the project objective and easy to parse.

It’s not just easier to read — it also feeds automated tooling that can process this standardized format.

Problems with non-semantic commits

Hard to understand changes: Vague messages like “Fixed bug” or “Updates” don’t give enough context. That makes it tough for devs to grasp the reasoning or impact of a change, especially on bigger projects.

No standard at all: Without an agreed convention, every dev writes commits their own way, leading to an inconsistent and sometimes confusing history.

Slower bug hunting: When you can’t clearly tell what each commit was meant to do, tracking down the origin of a bug or unexpected behavior gets way harder.

Semantic commit structure

The general format is:

<type>(<scope>): <message>

  • Type: The nature of the change, e.g., feat, fix, docs.

  • Scope: Optional. Tells where the change happened, e.g., auth, api, db.

  • Message: A concise description of what changed.

Semantic commit types

  • feat: New feature or capability added to the project. Example: feat(api): add user signup route

  • fix: A bug fix. Example: fix(auth): resolve JWT token validation issue

  • docs: Documentation-only changes. Example: docs(readme): update docker-compose setup instructions

  • style: Style tweaks like formatting or missing semicolons that don’t affect logic. Example: style(controllers): remove unused whitespace

  • refactor: Code changes that don’t add features or fix bugs — restructuring or optimizing. Example: refactor(db): move connection to dependency injection

  • perf: Performance improvements. Example: perf(queries): add index to speed up database lookups

  • test: Add missing tests or fix existing ones. Example: test(api): add unit test for login service

  • chore: Routine work or maintenance, like bumping dependencies. Example: chore(deps): bump Microsoft.EntityFrameworkCore to v8.0

  • Scope is not always mandatory, but at minimum keep the type: commit message pattern.

A commit example:

  • git commit -mfeat: implementing login button in header

Or, if you want to keep things in Portuguese, that works too:

  • git commit -mfeat: implementando botão de login no header

Breaking Changes

If your commit introduces a breaking change (like altering an API contract), the convention asks you to add a ! right after the type or scope.

  • Example: feat(api)!: change response shape of the users endpoint

The commit message

The message should be clear, concise and actually describe what was done.

  • Keep it short: Try to keep the message brief but informative. Ideally no more than 40–50 characters.

  • Skip the fluff: Phrases like “I added…” or “I changed…” are unnecessary. Get to the point: fix: resolve login issue with email.

  • Use the present tense: Keeps things consistent and easier to read. Instead of “added” or “fixed”, use “add” or “fix”.


Bonus tip: tools to automate this

To enforce this standard on bigger projects or in teams, there are tools that help or even force the pattern:

  • Conventional Commit (JetBrains Plugin): If you use Rider or another JetBrains IDE, this plugin is excellent. It adds an interface right inside the commit window where you select the type and scope and fill the message — and it formats everything for you.
  • Commitizen: Instead of typing git commit -m "...", Commitizen pops up an interactive prompt in your terminal that asks for type, scope and message, then builds the perfect semantic commit.
  • Commitlint: Acts like a gatekeeper — blocks commits if the message doesn’t follow the semantic rules.

References

If you want to go deeper, here are the materials I learned from plus the official spec: