One Claude Code Tip a Day: Make the Commit Message Prove the Diff

Do not let Claude Code write a decorative commit message. Make it read the diff, name the behavior, cite the proof, and expose skipped checks before you commit.

Editorial illustration of an AI coding assistant turning a git diff and test results into a structured commit message for code review.

This post is part of the “One Claude Code Tip a Day” series — a daily guide to using Claude Code more effectively.

The failure: a commit message that hides the real change

A bad Claude Code commit message usually sounds competent: `fix dashboard bug`, `improve validation`, or `update auth flow`. The problem is not grammar. The problem is that the message was written from the assistant’s memory of the task, not from the actual diff. In a real session, those two drift apart quickly. Claude starts with a frontend loading bug, touches a shared hook, adds a test fixture, removes a fallback branch, and then summarizes the commit as if only one component changed.

Today’s habit is to make Claude Code draft the commit message only after it has inspected the diff and the verification evidence. Treat the commit message as a miniature review document: what changed, why it changed, how it was proven, and what still deserves attention. If the message cannot point back to the diff, it is not ready to become history.

The workflow: make the diff the source of truth

Do not begin with `write a commit message`. Begin with a read-only review pass. In Claude Code, I use shell checks as the boundary between implementation and history:

Prompt: `Do not edit files. Run !git status --short, !git diff --stat, and inspect the full git diff. Then draft a commit message whose subject names the user-visible behavior, not the implementation detail. In the body, include: changed areas, why the change was needed, verification commands with results, skipped checks, and one reviewer note if the diff has risk.`

This prompt forces Claude to stop role-playing as a summarizer and become a diff reader. The subject should be specific enough that a teammate can scan the log later: `Prevent empty dashboard titles after AI generation` is better than `fix title bug`. The body should explain the engineering path: component class adjusted, regression test added, build passed, browser check still needed on Safari if you did not run it.

A realistic session: validation bug plus an accidental cleanup

Imagine Claude Code fixed a billing form bug where blank coupon codes were sent as empty strings instead of being omitted. The first patch updates `BillingForm.tsx`, changes `couponSchema.ts`, and adds a unit test. During the edit, Claude also renames a helper in `formatMoney.ts` because it noticed an awkward name. The tests pass, and the final response says the billing issue is fixed.

Now ask for the commit-message pass. A good Claude response should notice that the money-format rename is unrelated to the coupon behavior. The draft might say: `Omit blank coupon codes before billing submit`, then body bullets for the schema normalization, form submit path, and regression test. It should also include a reviewer note: `Unrelated helper rename in formatMoney.ts appears in the diff; consider reverting or splitting into a separate commit.` That note is the value. The commit message workflow does not merely polish words; it catches scope creep before you preserve it forever.

Second pass: challenge the first draft

Claude’s first commit message is often too broad. It may claim `validates coupons correctly` when the diff only handles blank strings. Challenge it with a second prompt:

Prompt: `Compare this commit message line by line against the diff. Remove any claim not directly supported by changed code or verification output. If the subject is broader than the actual behavior, narrow it. If a changed file is not represented in the message, either add a risk note or recommend splitting/reverting it.`

This is where the session becomes useful for real review. You are not asking for prettier Conventional Commits. You are asking Claude to make the message falsifiable. If it says `add coupon validation`, the diff should show validation. If it says `verified checkout flow`, the receipt should include the command or browser path that proves checkout. If it only ran `pnpm test coupon`, the message should not pretend the whole billing flow was exercised.

What to inspect before committing

Before accepting the result, inspect three things. First, compare the subject to the user-facing change. A subject that names an internal file is usually weaker than one that names the behavior. Second, compare the body to `git diff --stat`. Every changed area should be accounted for. Third, compare the verification paragraph to terminal history. Claude should list exact commands, exit status, and meaningful skipped checks, not `tests pass` as a vibe.

For higher-risk work, ask Claude to produce two versions: a concise commit message and a longer pull-request note. The commit message should age well in `git log`; the PR note can carry screenshots, migration cautions, rollout notes, or browser-specific checks. Do not overload the commit message with everything Claude knows. Make it accurate, not encyclopedic.

Failure modes and rule of thumb

The first failure mode is letting Claude write from intent instead of diff. The cure is always the same: run the diff commands first. The second failure mode is accepting a message that launders unverified claims. If the build failed, the body should say so. If Playwright was skipped because the dev server would not start, that is a useful review fact, not an embarrassment to hide. The third failure mode is mixing unrelated changes into one elegant message. A beautiful commit message for a messy diff is still a messy commit.

My rule of thumb: ask Claude Code for a commit message when you are ready to review, not when you are ready to stop thinking. A good message proves the diff by naming behavior, scope, evidence, and risk. If Claude cannot write that message honestly, the patch probably needs one more pass before it deserves a commit.