Stop Using Conventional Commits
165 points by dryya
165 points by dryya
Nice to see someone articulate some arguments against conventional commits that aren't just the visceral disgust I feel when I see them. I haven't thought much about why and kind of assumed that I've just come to associate them with LLM-generated code. I think the one I dislike the most is "chore:". Just stop reinventing hungarian notation. It should never have been invented in the first place.
I think the one I dislike the most is "chore:".
Especially since chore: is no longer part of the Angular commit style guide, and was subsumed into build:, because they must have realised how vague it was. Even when it was part of Angular's commit style, chore: had a description that gave specific intended purposes. The way it's used in some open source projects seems to be mostly based on vibes, for things that literally feel like a chore to do.
I don't think I've ever used chore: for anything other than dependency updates. Isn't that what it's mostly used for?
Tooling around the code: build scripts, CI pipelines, Dockerfile (if the goal was "improve the resulting Docker image", not "adjust Docker build to a change in application"), acting on linter notes…
I'm in the SRE team and basically all my commits to the application's repository were tagged "chore" when the repo was using conventional commits. Have to admit that this was kind of annoying.
I stand corrected: just checked the conventional commits website and it lists a number of other types, including "build", "ci", "docs", and more – and it's an open list. I remembered a closed (and very strict) list, and anything that wasn't a change in application code was categorized a "chore". Either I misremembered, or I was looking at an earlier version or some specific implementation of CC (and it's definitely coloured by how that particular team used CC)
The changelog autogen promise has always felt so bad to me. Commit messages are for people maintaining the system! Users require a different changelog.
Instead what I had in the past was simple: in a commit message (or a PR body or a PR commit) you could write a changelog line. @ReleaseBot releasenote: ..... Then the bot could collect that.
No annoying rebases, and someone could go in and add the release note easily during review for example.
I want to say, "Stop Using Monospace Fonts for Paragraphs of Text"
But also, I think i agree with the premise.
I'm not a fan of conventional commits either, but I think the proposed replacement overlooks one of the reasons that scopes are optional in conventional commits: for small projects without a lot of distinct modules, the concept of a "scope" isn't particularly useful.
One useful thing that's missing from both conventional commits and the proposed replacement, but that I've found pretty helpful in my day-to-day workflows, is to include an issue or ticket number in the commit title. That can make it a lot easier to figure out additional context for a change, especially during code review. I don't like requiring a ticket number because it leads to a proliferation of useless tickets for trivial changes. But any work that's servicing a particular bug or task should link back to the bug or task.
If you don't need scope then you can skip it! Still better than redundant "type" of commit that should be obvious from title line.
One useful thing that's missing from both conventional commits and the proposed replacement, but that I've found pretty helpful in my day-to-day workflows, is to include an issue or ticket number in the commit title. That can make it a lot easier to figure out additional context for a change, especially during code review.
In an ideal world, I feel we'd be able to forego any kind of prescriptive commit style in general, and just use whatever makes sense for a specific commit. So you could have "ticket number" commits where the changes clearly map to a ticket, and use something else when they don't. You could maybe even mix and match scoped commits with conventional commits, because some changes do clearly map to a type but less so to a scope, and vice versa.
No standard express explicitly how to manage links or tags into commits, and I think we should.
Many saas implies a ref to a merge eg (#124) within the platform but what about Jira task or confluence pages for example? Or any other type of link you may think of it.
I agree, that extra information that helps and we should use it.
No standard express explicitly how to manage links or tags into commits, and I think we should. [...] Or any other type of link you may think of it.
I would personally suggest RFC 3986.
Omitting a scope and ticket numbers are both addressed on the author's website for the method: https://scopedcommits.com/ (in the FAQ)
Should it be called: Stop Using Conventional Commits and Start Using Scoped Commits instead?
While I think Scoped Commits is interesting as case, I think is no different from Conventional Commits in the sense that optimise for a specific goal.
There is an underlying element structure behind both conventions that screams to be free.
These conventions are nothing but a specific case (instance) of these elements.
I hope and believe that at some points we'll collectively highlight these basic elements and let people arrange them as best suits for their case and that we'll start talking about the effects that emerge on moving these elements into the commit space.
I have to admit I fell for conventional commits a bit too hard as a way to encourage commit discipline, and then got stuck with them as a habit. Now I do often find them limiting, and arbitrary.
For some of my projects I'd drifted towards using the Linux/Go/Node/etc. style without realising it was a real convention. It just made sense, for example for a monorepo of diverse configs, to just say [service]: [what changed] rather than try to come up with a type. I'll probably experiment more with my personal commit style to try and find what looks useful, rather than fits a strict convention. But scoped commits feel like a good starting point.
chore(lobsters): add my 2 cents on conventionals commits [JIRA-69420]
very much agree with all but one thing:
It also presents a revisionist history to the contributors trying to contribute to the project, reducing the reliability of the story the commit log is telling.
i feel like the author is talking mainly about public branches (for which this is sane advice), but for private branches this shouldn't apply. make it make sense to whoever is reviewing the final change (be it a maintainer, or you in 10 years), instead of leaving an incoherent train of thought (or worse, a bunch of 'address review' commits).
I feel like both formats are impoverished attempts to shoehorn metadata into a text field, but given that we're stuck with it...
I'm sympathetic to the criticisms and goals of Scoped Commits, but I think it's missing a reason scope is so often left out of Conv Commits: it's not a useful signal when scope is broad.
One of the articles they cite, Conventional Commits, considered harmful, raises the same objection, which also applies to Scoped Commits:
Conventional commits encourages you to define a “scope” for each commit. The problem is that, in practice, commits frequently end up spanning multiple scopes.
The linked article doesn't directly address this, but over on scopedcommits.com, there's a section called "What if my commit covers multiple scopes?" that lays out some escape hatches, which acknowledges the problem but doesn't fix it:
If your commit covers multiple scopes, try one of the following approaches:
- Use a more general scope that encompasses all the relevant scopes.
- List both scopes, separated by a comma.
- If the commit touches the entire tree, you can use a scope such as
treewide,all, orglobal.If all else fails, just call the commit a “special commit”, ignore the scope, and write a good description.
I think the switch to emphasizing scope could be useful, but I'd want to see it in action before adopting it myself. My gut says the benefits will be modest at best for my use case (tiny team, private repos).
We use conventional commits at work to create automated semver releases, a pattern I've not used previously. I strongly dislike it.
Possibly my dislike is more relate to the associated GitOps nonsense, whereby bumping versions in an environment is a manual code change somewhere else. What the hell happened to continuous delivery?
But I tend to agree with the author (perhaps in less forceful terms) that conventional commit is sort of a band aid on writing bad commit messages.
I think there's something to be said for machine-readable metadata in commit messages, but to me it makes a lot more sense to have that data in commit bodies and not clutter up the subject line.
I suggest using git log --name-only or git log --stat a lot to get a feel for the scope of change even if the commit messages aren't great. Seeing the filenames can help a lot in knowing what was changed without showing each commit.
Why the hell is scope optional?
Because in a small project there's just a single project scope?
I agree that "type" of the commit is not all that useful, but I don't really see much of a difference between scoped and conventional commits anyway? Scoped is just conventional without the "type", and fix vs feat vs refactor vs chore is an OK distinction to have.
but pretty much everyone just takes the defaults from commitlint
Then we should make people handle it better?
Why the hell is scope optional?
Because in a small project there's just project scope?
I think the unspoken context here is large projects.
The "Conventional Commit" standard is not just for large projects, right? Nothing is stopping projects for tightening the rules and requiring the scope.
I'm personally more interested in chore/fix/feat than the scope. I can figure out the scope by other means, but I can't so easily figure out the intended ramifications of the changes.
This feels like an advertisement for scopedcommits.com disguised in arguments that leverage people's strong feelings about conventional commits.
We're allowed to have neutral feelings towards conventional commits. I know, because that's how I feel.
I think the author tapped into the knowledge that many people have strong, negative feelings towards conventional commits as a sleight of hand. He both recognizes that people use conventional commits for semantic versioning, and offers scoped commits without acknowledging that semantic versioning is both good, and not supported by scoped commits.
As the author points out, semantic commits don't perfectly solve semantic versioning. There are flaws in rollbacks/reverts that semantic versioning simply can't solve for you (as far as I know). But at least semantic commits offer something to those who use them to bump semantic versions.
On change logging, I found this argument frustratingly dull
Is this even a good idea? No! The audience of a changelog is entirely different than the audience for a commit log!
Developers don't need to be told who their audiences are. Commit message and changelog audiences are often the same people for library developers. And for developers writing applications for non-technical users -- we're smart; we know how to cater a message to an audience. We'll figure it out.
Developers are going to find ways to encode metadata any time they're given a text field. If they want to encode versioning and changelog hints in commit messages, who are we to stop them? Given a better alternative to do that, developers will naturally adopt one. Scoped commits aren't it, in my estimation.
advertisement for scopedcommits.com disguised
How is that an advertisement? It's not a product or a service, it's just a different delivery (the idea vs the arguments) of the same thing as the article.
What I really love is enforcing conventional commit's style in the PR title: it can be edited by maintainers even after the PR was merged, doesn't need commit history rewrite, and one can use them with tools such as release-drafter which automates meaningful changelogs on GitHub releases, at the correct granularity level for the stakeholders as the author complains: separated by features, fixes, breaking changes, and automatically takes care of setting a sane semver for the next GH release draft.
The author has a point that components (like parse-lib) shouldn't be optional. And I agree that enforcing conventional commits stifles new contributions. But the alternatives are not particularly better.
Still the breaking change identifier:
fix!(parse-lib): Don't leave sparse holes when parsing JSON arrays
gives a lot of info: a fix for a bug in a specific component, a breaking change that came with the fix, surely inevitable, a minor sem bump, etc. This can be used in the PR title.
I think the log consistency benefits are a valid argument for using conventional commits, so I generally use them. Sure, if I had more imagination, I could come up with my own conventions, but I am often uninspired. In the largest thing I maintain, I added some custom types that I think are useful (namely compat, cfix and imprv).
I liked that the article convinced me that scope is very important, I will try to change my practices about that, maybe even enforce it for my large projects. As other have said though, small libs and tools can have a single scope.
I disagree with the arguments against the automated changelogs:
A changelog is user-facing
A commit log is developer-facing
True for apps, but if you develop a library, your users are developers.
Nothing forces me to use the automated changelog as-is, and I generally don't. But I find it very nice to have this automated changelog as an editable basis for my final changelog. Maybe it's sunk cost fallacy, but I very much enjoy how my git-cliff template merge changelog entries with my cfix(parent-hash) type, where I abuse the scope part (cfixes are more or less "fixups" for merged yet unreleased commits).
fix(compiler): prevent namespaced SVG <style> elements from being stripped
it’s obvious that it was a bugfix
I disagree. Maybe it's a new feature, because it was expected that style elements were stripped?
refactor(core): Update webmcp support to use document.modelContext
was that a bugfix, refactor, or new feature? I would argue it’s all of them!
This looks like a feature to me, not a refactor. IMHO, ideally one should try to split the refactor needed for a feature and type it "refactor", then implement supporting the new thing in a "feat" commit. I like to use "imprv" as an alternative to "feat" for things that I don't think quite qualify as a new feature though. It is a custom prefix, so technically not a conventional one, so I guess I agree with the article's point about types being too restrictive.
to that I say “no”. A commit’s description should almost always tell you the type of the change!
While I agree, it doesn't mean the extracted type isn't useful. Attention is at a premium, and being able to do a first pass for "which commits may be relevant" when you're looking for change in behaviour is really useful. I can always go back to re-evaluate all the commits, but being able to visually immediately skip the "chore" entries is really useful to me.
The only useful property of conventional commits is that when you are about to make a commit starting with chore:, you know you should automate whatever you just did.
In my experience the subject line of a conventional commit is almost always less informative than those of commits following other conventions. I think choosing the tag takes an outsize amount of mental energy.
Weren't Linux commit descriptions supposed to start with a verb in imperative specifically? I guess it has gotten forgotten over the years...
I still try to use the most fitting one in my commits:
Fix, Avoid, Refactor, Polish, Improve, Format, Require, Enforce, Relax, Drop, Rename, Remove, Bump, Release...
The idea with Conventional Commits (and similarly the proposed Scoped Commits) is still to start with an imperative, but add the appropriate prefix.
chore: Update the CI image
feat: Add frobnostification option
or
treewide: Use a different allocator
ui: Switch to pink highlights
I have never heard of "conventional commits" (before clicking through I didn't even expect it to be a formal convention; I expected the post to be proposing something unconventional), but I have often found myself prefixing commit messages with "fix: ...".
it's possible that I do this due to being influenced by projects that were indeed following the "conventional" guidelines, but personally I do it because to my mind the default nature of a commit is to add some feature or capability to a project, and if the purpose of some particular commit is instead to fix a bug in the existing code (especially a minor bug rather than a deep structural flaw) it's worth tagging it. the "fix:" is not so much the "type" of the commit in a classification sense, it is rather the mindset of the commit, and it comes first to tell the reader to enter that mindset when reading the commit.
This commit updated the webmcp functionality in the core component to support both document.modelContext and navigator.modelContext, so was that a bugfix, refactor, or new feature? I would argue it’s all of them! But again, the only thing that really matters is that it was a change to the core/webmcp component.
This is actually why I think even if the other categories are confused, refactor is a very useful commit type.
A refactoring commit does not change functionality in any way or fix bugs—if it does so, you’ve made a mistake.
Sometimes a bug fix or functional change will include refactoring, but that doesn’t make it an refactoring commit. Also, ideally you’d separate any substantial refactoring into separate commits.
Personally, I end up using Yarn's Release Workflow in almost every project. It has a convenient interactive CLI for picking the type of version bump that should accompany a given branch (not commit), and it will automatically detect, based on changed files, what packages need a bump ("decline" is also an option).
It solves the problem that I suspect most folks use conventional commits for (automatic version bumps), but more explicitly and, in my opinion, with better scoping and tooling