← Tools I Use

Conventional Commits Optimizes the Wrong Field

2026-06-06 · ~6 min read · Conor Dobbs

A post titled "Stop Using Conventional Commits" reached the Hacker News front page this week, and the thread underneath it ran long. The complaint is not that structured commit messages are bad. It is that the structure Conventional Commits chose puts the least useful information first and makes the most useful information optional. I run a site that compares developer tools, so I spend a lot of time on the gap between a convention's promise and its payoff. This is one of the clearer examples.

Here is the format in question. Conventional Commits asks you to write type(scope): description, where type comes from a fixed set like feat, fix, chore, refactor, or docs, and scope is an optional parenthetical naming the part of the system you touched. The selling point is that machines can read the type, bump your semantic version, and generate a changelog without a human writing one.

The ordering is backwards

When you read a git log to find where a bug crept in, or to understand what a subsystem has been doing for the last month, the field you scan for is the area of the code. The type of change is something you can read off the description itself. A line that says "fix null check in the auth refresh path" does not need a fix: bolted onto the front to announce that it is a fix. Sumner Evans, who wrote the post that started the thread, states it plainly: Conventional Commits gets the priority of scope and type entirely wrong.

The format spends your scarcest resource, the front of a roughly 50-character subject line, on the field you needed least. Then it lets the field you needed most, the scope, fall off as optional. In practice most teams drop the parenthetical because it is extra typing for no enforced reward, so the log fills with feat: and fix: and chore: prefixes that carry almost no signal. You learn that something is a feature before you learn what part of the product it touched.

The changelog promise does not hold

The headline benefit is automated changelog generation. The trouble is that a commit log and a changelog serve different readers. A commit log is for the people maintaining the code, and it talks about internals: which function, which guard, which migration. A changelog is for users, and it talks about behavior: what is new, what broke, what they need to do before upgrading. Evans makes the point that any attempt to merge the two produces a subpar result for both. I have read enough auto-generated changelogs that read like a raw git log dump to agree. A list of forty fix: lines is not a changelog. It is a commit log wearing a changelog's name tag.

Semantic versioning has a similar gap. The theory is that a feat bumps the minor version and a breaking change bumps the major, so the tool can version your release for you. Reality includes reverts, accidental breakage shipped under an innocent fix, and behavior that turns out to be breaking only after a user reports it. The commit type is a guess made at write time about consequences that are sometimes known only later. Trusting that guess to drive your public version number moves a human judgment call into a string prefix and hopes nobody got it wrong.

There is also a quieter failure mode worth naming for anyone wiring CI off commit types. If a pipeline triggers privileged steps based on the prefix, the prefix becomes an input an attacker can set. A commit labeled to look routine can carry something that is not. Driving automation off a free-text field that the author controls is a pattern to treat with care, not a free win.

What the critics propose instead

The alternative in the thread is not "write whatever you want." It is to flip the priority: lead with scope. Large, long-lived projects already do this. The Linux kernel, Go, and FreeBSD use a subsystem: description style, so a log line opens with the part of the system in play and then says what changed. auth: fix null check in refresh path reads cleanly and front-loads the thing you grep for. You keep structure and machine-readability, and you spend the subject line on signal instead of category.

That style also degrades better. If someone writes a vague scope, you still get a human-readable sentence. If someone skips the Conventional Commits type, linters in many repos reject the commit outright, which is how you end up with developers typing chore: to get past the hook rather than thinking about the message.

The honest other side

I am not going to pretend Conventional Commits never pays off. There is a real cohort where it earns its keep: large open-source libraries with many outside contributors and release tooling already wired to it, where the type prefix feeds tools like semantic-release and the maintainers genuinely want machine-driven versioning. If you are shipping a popular package on npm with dozens of drive-by pull requests, the rigidity is a feature, because it gives strangers a fixed shape to follow and gives your release bot something deterministic to read.

The mismatch shows up when a small team copies that setup for a private application. A product repo with four engineers and no public release cadence gets the ceremony of the convention and almost none of the payoff. Nobody is consuming the generated changelog. Nothing bumps a public version. The commit hook becomes a tax you pay to satisfy a linter, and the team slowly learns to write the shortest prefix that passes rather than the most useful message.

Where I land

The lesson here is one I keep relearning across tools: a convention is only worth its overhead if the thing it produces gets consumed. Conventional Commits is built for a release-automation pipeline. If you have that pipeline and people who read its output, the structure is reasonable, even if I would still argue the field order is wrong. If you do not, you have adopted the cost of a system whose benefit lands on someone else's desk.

A cheaper default for most teams is a scope-first message and a hard rule that the subject line says what changed and where. Reserve the heavy machine-readable convention for the repos that have a machine reading it. That is the same test I apply to each tool I write up: not "is this a best practice somewhere," but "does the output of this practice have a reader on my team." If the answer is no, the practice is decoration, and decoration that runs on a commit hook is the most annoying kind.

If you spend your days weighing this kind of tradeoff, my site at tools.thesoundmethod.me works through developer-tool and workflow choices the same way: what the thing claims, what it costs, and who the payoff is for.