How to write a Git commit message — for future you and for AI

Open the history of any learning project and you'll see something like this: fix, fix2, changes, finally works. Written in a second, and it feels harmless: the code is what matters, right?
Now the twist: you don't write a commit message for today-you. It has two real readers — you six months from now, who forgot everything and is hunting for where things broke, — and an AI agent reading the project history to understand what happened here. To both of them, fix2 says nothing. Let's write ones that do.
Step by step
-
First, look at what you're committing. Before writing anything, run
git statusandgit diff— refresh what actually changed. A good message describes the real changes, not "what I think I was doing." If the changes span three unrelated topics — split them into three commits: one commit = one meaningful change. -
Write the subject: what the commit does, up to 50 characters. The formula — continue the sentence "this commit … ": "adds email validation," "fixes the empty-cart crash." The convention is to write it in the imperative mood:
add email validation,fix cart crash— short and uniform:git commit -m "fix crash when cart is empty" -
Add a type prefix — one word with a colon at the front:
feat:(new functionality),fix:(a repair),docs:,refactor:,chore:(routine). That's the conventional commits convention — understood by people, tools, and models alike:git commit -m "fix: crash when cart is empty" -
If the reason isn't obvious — add a body: the why. A second
-madds an explanation paragraph. What changed is visible from the code; the message should show why:git commit -m "fix: crash when cart is empty" -m "The total() call assumed at least one item. Guard added because guest users can open the cart before adding anything." -
Check yourself with a stranger's eyes:
git log --oneline -5prints the last five subjects. Does it read like a coherent story of the project? Great. See afix2in there — now you know what to do with it.
What you get
A history you can navigate without archaeology:
feat: add email validation on signup
fix: crash when cart is empty
docs: add setup steps to README
refactor: extract price logic into helper
With a history like this you instantly see where to look when something breaks ("what did we touch recently?"), it's a pleasure to show in a portfolio — and, most practically, rollbacks become deliberate: when you need to undo a commit, you'll be choosing between clear lines instead of guessing what hides behind fix2.
Good and bad — by example
- ❌
changes→ ✅feat: add dark theme toggle— every commit has "changes"; that's zero information. - ❌
fixed bug→ ✅fix: avatar upload fails for files over 5 MB— which bug? There will be dozens. - ❌
feat: added new feature and fixed bugs and updated readme→ ✅ three separate commits — "and… and… and" in a subject is the signal to split. - ❌
WIP,asdf,final-final2→ ✅ any honest subject: even a plainfix: typo in login formbeats them.
Where AI comes in
Two practical points. First: AI agents like Claude Code read git log when they explore a project — a clear history literally makes the agent smarter in your repo; it's part of the context. Second: you can delegate the message itself — ask "write a commit message for this diff" and paste the output of git diff. The model is good at the "what," but only you know the "why" — add the reason behind any non-obvious decision yourself. The rest of the everyday commands live in Git commands every beginner needs.
Which language should I write in?
Pick one language per project and stick to it. Open source and work projects — almost always English. A personal project — whatever you like, but remember: English messages are understood by more tools, and a portfolio reads more conventionally with them.
Does every commit need a long body?
No. Small obvious changes are fine with just a subject. The body is for places where future you would ask "why was this done?" — a non-obvious reason, a workaround for someone else's bug, an important decision.
What if I already pushed a commit with a bad message?
The latest unpushed commit can be rewritten: git commit --amend -m "new message". If the commit already went to a shared branch on the server — don't rewrite history; just write better starting with the next one.
Short story-lessons, an agent simulator and daily practice — in our mobile app. Free.





