How to Write Good Commit Messages With AI

AI coding agents default to commit messages that restate the diff. A Conventional Commits template and a two-question review habit fix that.

Steve Jefferson
Steve Jefferson
Developer Advocate
6 August 20261 min read

A commit message written by an AI coding agent defaults to describing what changed in the diff: "update auth.ts", "fix bug", "refactor component". That is worse than useless, because the diff already shows what changed. A commit message earns its place by saying why, and that is exactly the part a model has to be told to include, because it is not visible in the code it just wrote.

The fix is not to write commit messages yourself again. It is to give the agent a structure that forces the why to the surface, and to review before you let it commit.

Why AI-generated commit messages default to bad

A coding agent sees the diff, the file tree, and whatever you told it in the prompt. It rarely sees the ticket, the bug report, or the conversation that led to the change. Left to its own defaults, it summarizes the patch, which produces messages like "update payment handling" on a commit that actually fixes a race condition that double-charged customers. The summary is true. It is also nearly worthless six months from now when someone runs git blame trying to understand why that line exists.

Three failure patterns show up constantly:

  • Diff-restating. "Add validation to signup form" instead of "Reject signups with disposable email domains, per the spam wave from last week."

  • Vague verbs. "Update", "fix", "improve", "change" with no object worth reading.

  • Missing scope. One commit message covering four unrelated changes because the agent was told to "clean up the file" and did, in one shot.

Use Conventional Commits as the scaffold

The Conventional Commits specification gives an agent a format to fill in rather than a blank page to improvise on: type(scope): description, optionally followed by a body and a footer. Feeding an agent a spec instead of vague good taste is exactly the kind of thing models are reliable at.

Type

Use for

feat

A new capability a user or API consumer can now do

fix

A bug that previously produced wrong behavior

refactor

Code changed, behavior did not

perf

Measurably faster or lighter, same behavior

docs

Documentation only

test

Tests added or changed, no production code

chore

Tooling, dependencies, config

The type alone kills a chunk of the vagueness problem, because "refactor: extract validation into a shared helper" cannot collapse into "update file" the way an untyped message can.

A prompt template that produces reviewable messages

Paste this into your agent's instructions, system prompt, or a CLAUDE.md-style memory file once, and stop thinking about it per commit:

"When committing, use Conventional Commits format. The subject line is under 72 characters, imperative mood ('add', not 'added'), and names the type and scope. If the change fixes a bug, the body states what was broken and the user-visible effect, not just what code moved. If a commit would bundle unrelated changes, stop and ask whether to split it into separate commits instead of writing one message that covers all of them."

That last sentence matters more than the formatting rules. An agent that bundles four changes into one commit because it was told to "finish the cleanup" is a bigger problem than an agent that under-formats a message, and it is the harder failure to catch by reading the message alone, since the message can sound reasonable while covering ground it should not.

Before and after

Agent default

With the template

update auth.ts

fix(auth): reject expired refresh tokens instead of silently reissuing them

fix bug

fix(checkout): stop double-charging when the payment webhook retries

refactor code

refactor(api): extract rate-limit check into shared middleware, no behavior change

changes

feat(export): add CSV export for the transactions table

Every entry in the right column answers a question a future reader will actually ask: what broke, what does the user see now, did behavior change. That is the bar, not length or vocabulary.

Review before it commits, not after

Treat an agent's proposed commit message the way you would treat its code: read it before it lands, not after. Two checks catch most of what goes wrong:

  1. Does the message match the diff, not the request? An agent asked to "add rate limiting" that also touched an unrelated logging line should either mention both changes or drop the second one from this commit.

  2. Would this sentence mean anything without the diff open? "Update handler" fails this. "Reject signups with disposable email domains" passes.

For anyone using an AI coding agent day to day, this is the same discipline covered in how to review AI-generated code before you ship it: the commit message is part of what you are reviewing, not an administrative afterthought that happens after review is done.

A worked example

Say you ask an agent to "add pagination to the users list and fix the flaky sort test while you're in there." Without guidance, that often lands as one commit: "update users list". With the template in place, a well-behaved agent instead produces two commits, because the instruction told it to stop and split unrelated changes:

  1. feat(users): add cursor-based pagination to the users list endpoint. Body: returns 25 records per page by default, accepts a `cursor` query param, response includes `next_cursor`.

  2. test(users): fix flaky sort test by seeding a deterministic timestamp. Body: the test compared records with equal `created_at` values, so ordering was undefined between runs.

Neither message is long. Both tell you exactly what to expect if you revert one of them, which is the actual test of whether a commit message did its job.

Where this breaks down

Squash-merge workflows make per-commit message quality nearly irrelevant, since only the final merge message survives in history. If your team squashes, spend the effort on the pull request title and description instead, and let the agent write loose intermediate commits without ceremony.

And a well-formatted message cannot rescue an agent that does not know why it made a change. If you never told it the ticket number, the bug report, or the reason for the request, the body it writes will still be a guess dressed up in good formatting. Give it that context in the prompt, the same way you would brief a teammate picking up the ticket, and the messages that come back stop being generic. That habit pairs directly with how to give AI context about your business, which is really the same problem: an agent writes better output when it knows the reason behind the request, not just the request itself.

If you use git with an AI coding agent regularly, commit hygiene is one of the cheapest habits to fix, because it is a prompt change, not a workflow change. It sits alongside the other small-habit fixes rounded up in AI coding tools: none of them individually matter much, and the set of them together is the difference between a repository you trust and one you re-review from scratch every time.

FAQ

Should I let the agent commit automatically, or review first?

Review first, at least for anything landing on a shared branch. Auto-committing is fine on a scratch branch you will squash anyway.

Does the commit type matter if my team doesn't use semantic versioning?

Less, but it still forces the agent to categorize the change instead of describing the diff, which is where most of the quality gain comes from regardless of whether you consume the types downstream.

What if the agent is making many small commits?

That is usually fine and often better than one large commit, as long as each message stands on its own. Ask it to keep commits scoped to one logical change rather than batching for fewer messages to write.

Can I enforce this automatically?

A commit-msg hook that checks for a valid Conventional Commits type will reject malformed messages before they land, which is worth adding once the format is part of your workflow.

How did this land?

About the author

Steve Jefferson
Steve Jefferson

Developer Advocate

Steve builds something with Swarmz every week and writes up what worked, what broke, and what he'd do differently. Tutorials and hands-on guides are his lane.

Share

Get the next post in your inbox

One email a month. Product updates, engineering posts, and the best of Built with Swarmz.

I agree to receive emails about AI building tips and Swarmz product news. Unsubscribe any time.