How to Write Good Git Commit Messages (and Use Conventional Commits)

Good commit messages turn Git history into documentation. In this lesson you will learn the anatomy of a great commit message, the Conventional Commits standard used by many teams, and how to enforce it automatically.

Advertisement

๐Ÿ“– What You’ll Learn: why commit messages matter, the subject, body and footer rules, bad vs good examples, Conventional Commits types and scopes, breaking changes, benefits like automated changelogs, enforcing the format with commitlint and Husky, and examples for test projects.


โœ๏ธ Why Commit Messages Matter

Six months from now, someone (probably you) will run git log or git blame to understand why a line of code exists. A message like “fix” or “update” tells them nothing. A good message saves hours of investigation, makes code review easier and can even generate release notes automatically.


๐Ÿ“ The Anatomy of a Good Commit Message

Fix flaky checkout test by waiting for the order API

The test clicked "Finish" before the cart total was recalculated,
so it failed about 1 in 20 runs in CI. Wait for the /api/order
response instead of using a fixed 2-second timeout.

Closes #87
PartRules
Subject lineAbout 50 characters (hard limit around 72); imperative mood (“Fix”, not “Fixed” or “Fixes”); capitalised; no full stop
Blank lineSeparates subject and body (tools rely on it)
Body (optional)Wrap at about 72 characters; explain what and why, not how (the diff shows how)
Footer (optional)Issue references (Closes #87), BREAKING CHANGE:, co-authors

๐Ÿ’ก Tip: A simple test for the subject line: it should complete the sentence “If applied, this commit will ___”. “If applied, this commit will fix flaky checkout test by waiting for the order API.”

Write multi-line messages by running git commit without -m; Git opens your configured editor.


โŒ Bad vs โœ… Good Examples

โŒ Badโœ… Good
fixFix tax rounding on the checkout summary page
update testsAdd negative login tests for locked-out users
changes as per reviewUse getByRole for the login button
WIPSquash WIP commits before merging (interactive rebase)
Fixed the bug where the thing didn't work on Safari when the user...Fix date picker on Safari (details in the body)

๐Ÿงฉ Conventional Commits

Conventional Commits is a widely used specification that adds a type (and optional scope) to the subject:

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

[optional body]

[optional footer(s)]
TypeUse forExample
featA new featurefeat(cart): add discount code field
fixA bug fixfix(checkout): round tax to two decimals
testAdding or fixing teststest(login): add locked-out user scenario
docsDocumentation onlydocs: add setup steps to README
refactorCode change that neither fixes a bug nor adds a featurerefactor(pages): extract BasePage
styleFormatting, whitespace (no code meaning change)style: apply Prettier formatting
perfPerformance improvementperf(api): cache product list
buildBuild system or dependenciesbuild: upgrade @playwright/test
ciCI configurationci: shard Playwright tests across 4 jobs
choreMaintenance that does not fit elsewherechore: update .gitignore
revertReverting a commitrevert: undo feat(cart) discount field

Breaking changes

Mark changes that break compatibility with ! or a footer:

feat(api)!: rename /users endpoint to /accounts

BREAKING CHANGE: clients must call /accounts instead of /users.

Why teams adopt Conventional Commits

  • History is easy to scan and filter (git log --oneline --grep="^fix").
  • Tools can generate changelogs and choose the next version automatically (fix โ†’ patch, feat โ†’ minor, breaking โ†’ major; see semantic versioning).
  • PR titles in the same format become clean commit messages when using Squash and merge (pull requests).

๐Ÿค– Enforce the Format Automatically

Teams usually enforce the format with commitlint running in a Git hook managed by Husky:

npm install -D @commitlint/cli @commitlint/config-conventional husky
echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.mjs
npx husky init
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg

Now a commit message like update stuff is rejected before the commit is created. Hooks are covered in Git Hooks and Husky.


๐Ÿงช Commit Messages for Test Automation Projects

test(checkout): add data-driven tests for invalid postcodes
fix(tests): replace waitForTimeout with web-first assertion in search spec
refactor(pages): move cart locators into CartPage
ci: upload Playwright traces on failure
build: bump @playwright/test to the latest version

๐Ÿ“ Assignment

  1. Rewrite these messages properly: “fix”, “tests”, “updated readme”, “final changes”.
  2. Make three commits in git-practice using Conventional Commits with different types and scopes.
  3. Write one commit with a subject, a body explaining why, and a Closes #<n> footer, using your editor instead of -m.
  4. Run git log --oneline --grep="^test" to list only test commits.
  5. Bonus: set up commitlint with Husky and try to commit a bad message.

โ“ FAQ

๐Ÿค” How do I write a good Git commit message?

Use a short imperative subject (about 50 characters), a blank line, and an optional body explaining what changed and why, with issue references in the footer.

๐Ÿค” What are Conventional Commits?

A specification for commit messages in the form type(scope): description, such as fix(login): handle locked users, that enables readable history and automated changelogs and versioning.

๐Ÿค” Should commit messages be in past or present tense?

The common convention is the imperative mood: “Add test”, “Fix bug”, as if giving a command.

๐Ÿค” What does feat! mean in a commit message?

The ! marks a breaking change, which usually triggers a major version increase.

๐Ÿค” How do I write a multi-line commit message?

Run git commit without -m to open your editor, or use -m twice: git commit -m "Subject" -m "Body".


๐ŸŽฏ Next Steps

๐ŸŽฌ Learn Git and GitHub step by step: watch the series on YouTube!

Subscribe to SqaToolsByDY • Practice on sqatools.in

2 thoughts on “How to Write Good Git Commit Messages (and Use Conventional Commits)”

Leave a Comment