I used to write commit messages such as fix bug or update code. They felt
good enough while the change was still fresh in my head.
A few months later, they were not useful at all. When I needed to understand an old change during debugging, the history could not tell me what had been fixed or why I had changed it.
That experience made me pay more attention to commit messages. This is not a rule that every repository must follow exactly. It is the structure that helps me—and the people I work with—understand changes later.
A Simple Structure
The format I commonly use is:
<type>(<optional scope>): <subject>
<optional body>
<optional footer>
For example:
fix(checkout): prevent duplicate cart items
Ignore another add request while the first request is still running.
Fixes: #123
The subject is enough for a small change. The body and footer are useful only when they preserve context that the subject cannot.
Choose a Type That Describes the Change
The types I see most often include:
| Type | Use |
|---|---|
feat |
Add user-visible behavior |
fix |
Correct broken behavior |
docs |
Change documentation |
refactor |
Restructure code without intentionally changing behavior |
test |
Add or update tests |
perf |
Improve performance |
build or ci |
Change the build or delivery configuration |
chore |
Perform maintenance that does not fit the other types |
This list is not universal. A team can use fewer types, different names, or no prefixes at all. Consistency matters more than including every possible type.
Write a Useful Subject
I try to make the subject describe the result of the change:
Add user registration endpoint
Reject expired checkout sessions
Document local installation steps
Vague subjects leave the important part hidden:
update
fix issue
minor changes
Using the imperative form—Add, Fix, Remove—keeps subjects consistent.
Around 50 characters is a useful readability target in many Git interfaces,
but I do not remove important meaning just to meet an exact number.
Add a Body When the Reason Is Not Obvious
The diff shows how the code changed. A body is useful when the reason or effect would otherwise be lost:
fix(import): report a failed database restore
Check the mysql client exit status before marking the import complete.
The previous command treated a return to the shell as success.
I use the body to explain why the change was necessary, an important trade-off, or something a reviewer should verify. I do not repeat every line already visible in the diff.
Conventional Commits Is Optional
Conventional Commits defines a machine-readable version of this structure. It can help when a project uses commit messages for changelogs or release versions.
feat(auth): add password reset
fix(cart): prevent duplicate items
docs: clarify setup instructions
A breaking change can use ! or a BREAKING CHANGE: footer. That convention
is useful only when the repository and its tooling agree on what it means.
Adding a prefix does not make a vague or unrelated commit clearer by itself.
The Check I Use Before Committing
Before finalizing a message, I ask:
- Does the subject say what changed?
- Is the type appropriate for this repository?
- Does the body explain context that the diff cannot?
- Is an issue or breaking change worth mentioning?
- Will I understand this message several months from now?
Writing a clear message takes a little more effort than update code, but it
saves time when I return to the history later. That is the main reason I keep
using the convention.