Conventional commits are a waste of time
Published on
Updated on
Conventional commits are an increasingly popular convention for naming commits. In essence, it provides a set of prefixes that should be applied to every commit message, thus categorising the commits. From this machine-readable information, the conventional commit adherents purport to derive a slew of useful information. This would result in time saved and thus easier lives for developers. Yet some would argue it does none of these things. It sacrifices valuable commit message space for prefixes with questionable usefulness.
We looked at great commits in the last piece, so now we all know what such a commit looks like. Here, we will look at what conventional commits are, what the supposed benefits are, and how none of these benefits are real.
Popularity of conventional commits
The lead of this article boldly claims that conventional commits are an increasingly popular way of writing commit messages. Some online encyclopediæ would add “Citation needed” to such a statement. Ask and you shall receive.
GitHub is without a doubt one of the biggest sources of public commits, since it has basically come to dominate the online Git-hosting space.11 Although many alternatives do exist. So, the idea is as follows: can we look at all public commits on GitHub and see how many of those follow the conventional commit standard?
To do this, we first need a dataset of all public commits on GitHub. Luckily for us, this exists: GH Archive, with archives going back to February 12th, 2011. So, more than ten years of data.
Next, how do we analyse this enormous dataset? We are lucky again: the dataset is publicly available on Google BigQuery, where we can run queries against it. The query executed does the following: for every commit message, it matches the commit against a regex. It then counts the number of commits that match against the total number of commits.
The regex in question is:
^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([\w\-.]+\))?!?:\sTo reduce the amount of data we need to process, the query filters the data:
- Excluded data from before 2015 (about ten years ago).
- Only look at two days per month, the 10th and 20th of each month.
The full query processed about 2 TB of data. Through the magic of BigQuery, after about ten seconds, the results are available. The percentage of commits that follow the conventional commits convention is plotted below in Figure 1.
From this image we can see a steady rise in the percentage of commits that adhere to the standard, ending with about 10% of all public commits on GitHub. So, our initial statement is correct: while conventional commits are far from the norm, they are increasingly used.
Of course, this is not scientific, peer-reviewed research. Some limitations apply:
- We assume private commits and public commits have the same distribution, but one might argue that “professional” commits are more likely to follow the standard than hobby commits, and professional commits are more likely to be private.
- The dataset was not validated. Recently, some concerns were raised about the completeness of the data.22 Issue № 310
- The query was not validated to not contain bugs.
- We only look at the same two days in every month, which could fall on weekends or public holidays.
- The query makes an assumption about which prefixes to count, which were not validated.
- We count all commits, including merge commits and other automated commits, a lot of which are not conventional commits. The results are thus probably underreporting the ratio if only considering human-written commits.
Nonetheless, the results can validate that conventional commits are on the rise. This matches the conclusion of the only literature we found that directly addresses this question.33 Zeng, Q., Zhang, Y., Qiu, Z., & Liu, H. (2025). A First Look at Conventional Commits Classification. In IEEE/ACM 47th International Conference on Software Engineering (pp. 2277–2289) In it, Zeng et al. address this in RQ1: “How common do projects apply Conventional Commits Specification?”. They follow a different approach to collecting data, but the conclusion is the same:
The Conventional Commits Specification (CCS) has gained increasing popularity among open-source projects and developers. On GitHub, a growing number of popular projects are adopting the CCS as their commit convention. In projects that have not adopted CCS, approximately 10% of the commits submitted by developers in 2023 adhered to CCS.
Satisfied that there is enough proof to make the claim, we will next look at conventional commits themselves: what they are.
Conventional Commits
Let us now take a closer look at those conventional commits. Conventional Commits is a specification that instructs programmers on how to structure commit messages. In its own words, it is a “lightweight convention on top of commit messages” and uses the tagline “A specification for adding human and machine-readable meaning to commit messages”. The final version was released in September 2019 and was originally based on the Angular Commit Conventions.
As a reminder or quick introduction, this is the format required by conventional commits:
<type>[optional scope][!]: <description> [optional body] [optional footer(s)]
Let’s take a closer look at the different sections, first quoting relevant parts of the specification. Note that we won’t go through everything, but only the essentials.
Type
- Commits MUST be prefixed with a type, which consists of a noun,
feat,fix, etc., followed by the OPTIONAL scope, OPTIONAL!, and REQUIRED terminal colon and space.- The type
featMUST be used when a commit adds a new feature to your application or library.- The type
fixMUST be used when a commit represents a bug fix for your application.- Types other than
featandfixMAY be used in your commit messages, e.g., docs: update ref docs.
Thus, we have two predefined types. One immediate “problem” with this is: how does one determine if a commit is a fix or a feature? While some commits are clearly one or the other, one could say that the type of commits is a spectrum: some commits might be both or difficult to classify.
For example, a feature in an application is missing due to a forgetful developer. Is that then a new feature (because you are technically adding new capabilities) or a fix (since it should always have worked)? This opens the door to endless bikeshedding: in an optimal case, technical decisions should either be made objectively or be completely a style choice. Semi-objective decisions inevitably invite discussion.
The webpage does provide an example of an extended list of commit types: build, chore, ci, docs, style, refactor, perf, and test.
Turning to the world-wide web, we find a definition in the Angular Commit Message Guidelines:
build Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
ci Changes to our CI configuration files and scripts (examples: GitHub Actions, SauceLabs)
docs Documentation only changes
feat A new feature
fix A bug fix
perf A code change that improves performance
refactor A code change that neither fixes a bug nor adds a feature
test Adding missing tests or correcting existing tests
First, note that this list does not include the ever popular chore type, which is generally used for code base maintenance in code-adjacent tasks.
Next, this expanded list with definitions does not solve the issue.
For example, the difference between perf and refactor is up to the intent of the commit author.
Scope
- A scope MAY be provided after a type. A scope MUST consist of a noun describing a section of the codebase surrounded by parentheses, e.g., fix(parser)
First, this is only useful in a subset of repositories, which is probably why the specification makes it optional. Secondly, the specification hints that the scope should be a technical scope. However, the technical scope of a commit (or rather, the “noun describing a section of the codebase”) could be determined by looking at the changes.
Note that some large repositories do benefit from adding a scope to commits. One such example is the Linux kernel. Scopes can be a good practice if they convey useful information to people reading the commits. In an ideal world, our tooling would determine this from the changes themselves, but sometimes we need to be pragmatic.
Description
- A description MUST immediately follow the colon and space after the type/scope prefix. The description is a short summary of the code changes, e.g., fix: array parsing issue when multiple spaces were contained in string.
The description is similar to the description recommended by Git itself, so there are no issues with it. However, we can look at what effect the other requirements have on the description.
For example, consider the following commit
refactor(platform-browser-dynamic): switching to relative imports within the platform-browser-dynamic package (#60559)
This commit updates scripts within `packages/platform-browser-dynamic` to relative imports as a prep work to the upcoming infra updates.
PR Close #60559This commit message violates a lot of the “rules” we set out last time:
- The title is 119 characters long, way more than 50 characters. 36 of those are spent on prefixes. Meaning that you only have 14 characters left for the actual title. Once the limit is broken, the incentive for brevity vanishes.
- The issue number is repeated in the title (and the pseudo-trailers).
- The description just repeats the title with more words. The only new information is “prep work to the upcoming infra updates”.
- The trailers do not follow Git’s established format for trailers.
Let us look at another commit:
refactor(platform-browser-dynamic): relocate DOMTestComponentRenderer to @angular/platform-browser (#60453)
This commit moves `DOMTestComponentRenderer` to `@angular/platform-browser/testing`, allowing the Angular CLI to eliminate its dependency on `@angular/platform-browser-dynamic`, which would no longer be required for new projects.
PR Close #60453Again we make the same observations:
- The title is 108 characters long.
- The description just reiterates both the title and changes obvious when looking at the diff.
- The line length of the body is 230, way more than 72 characters.
- The same pseudo-trailers are used.
Now, in Angular’s defence, these are some of the worst examples in the repository. However, their commit guidelines say nothing about length: both these commits satisfy their requirements.
Also, these are not inherently bad commits because they follow conventional commits. The convention itself does not necessarily result in these bad practices. But, at the same time, the convention does consume previous title space and incentivises metadata and changelog text above change rationale. The convention thus nudges you towards ignoring the general commit guidelines.
Finally, with the advent of wider monitors, code has become wider as well: line length caps are now often 120 characters instead of the traditional 80 characters. Even if we abandon the 50-character limit, the information density of conventional commit messages decreases: it takes a lot more characters to express the same amount of useful information.
Supposed benefits of conventional commits
As shown in the previous section, there are a bunch of downsides to conventional commits. However, if the upsides outweigh the downsides, it might be worth doing after all. So let’s look at those benefits. The website has a subsection titled Why Use Conventional Commits, in which it says:
- Automatically generating CHANGELOGs.
- Automatically determining a semantic version bump (based on the types of commits landed).
- Communicating the nature of changes to teammates, the public, and other stakeholders.
- Triggering build and publish processes.
- Making it easier for people to contribute to your projects, by allowing them to explore a more structured commit history.
Let’s go through these one by one.
Automatically generating changelogs
Since it is the very first reason they give, one might assume it is one of the primary motivators to adopt this convention. However, this statement completely bypasses the fact that in most cases, changelogs should not be automatically generated, at least not from commit messages.
In an ideal world, a changelog should inform end users about what changed, why they should upgrade, and if applicable, how they should upgrade. This is true for both software used by end users and software used by other developers. An automatically generated changelog from Git commits, however conventional they might be, fails at this:
- If the commits are the correct granularity for generating changelogs, the commits are way too big.
- If the commits are the correct granularity for recording code history and change rationale, they are way too granular to be included in changelogs.
- The generated changelog will not contain clear, logical upgrade instructions.
If you must generate a changelog, you should do so from pull requests (or other change requests systems, like patch sets or merge requests).44 If you are squashing your commits, see the previous article why you should not. Nothing is stopping you from looking at the commit list of a release and using that as a basis, but for that you don’t need conventional commits.
Let’s assume by using some arcane magic, your commits are ideal for generating a changelog, which can just consist of the commits included in a certain version. In that case, what is the benefit of having a changelog at all? People can just look at the commit list instead.
Secondly, an important part of changelogs is indicating an upgrade path for breaking changes. In many cases, this needs to be a guide, which is ill-suited for inclusion into commits. Similarly, the order in which migrations must happen does not necessarily map to the commit order.
Automatically determining a semantic version bump (based on the types of commits landed)
A relatively big part of conventional commits is dedicated to indicating breaking commits.
The exclamation mark is intended to call attention to a breaking change.
In the “trailers”, the only predefined tag is BREAKING CHANGE, in all caps.
They provide this example:
chore!: drop support for Node 6
BREAKING CHANGE: use JavaScript features not available in Node 6.Conventional commits are not all bad ideas: there is nothing wrong with attempting to automatically determine breaking changes through commits.
E.g. prefixing with ! or even including it in the trailers is not a problem.
However, its usefulness extends only to those situations where semantic versions are useful, which generally excludes end-user software. It is practically impossible to reliably determine breaking changes in any user-facing product (e.g. with user interfaces), so many products don’t.
Communicating the nature of changes to teammates, the public, and other stakeholders
We’ll tackle this together with the last reason below.
Triggering build and publish processes.
Again, just like determining the versions, there is nothing inherently wrong with this. Anecdotally, most projects do things differently, relying on branches or tags to determine building or publishing. They offer no example of this, but tooling exists that uses this, like semantic-release.
This tool uses the type of the commit to determine the next semantic version, e.g. a fix type triggers a patch release, a feature triggers a minor release, and a breaking change results in a major release. While semantic versioning itself is much less controversial, not everyone agrees it is useful enough to completely automate the whole release process.55 Semantic Versioning Will Not Save You by Hynek Schlawack
I have rarely encountered this in the wild, except for some JavaScript packages, in which case you will be bumping versions every week. So if you need this, you’ll probably need conventional commits, but I don’t know enough about it to have an opinion.
Making it easier for people to contribute to your projects, by allowing them to explore a more structured commit history.
This is basically part of the fourth benefit, so why they are listed separately is unknown. But let’s look at the benefits of communicating change.
To teammates, most of the information is useless. They are most interested in what has changed (the diff in the commit) and the rationale behind it (what the commit message should be). It rarely matters to others if you consider your commit to be a fix or feature. The scope is similarly fairly useless: teammates probably have a good idea of which area a commit affects or are interested in the real scope as determined by the changes, not what you consider the scope to be.
For potential new contributors, which they feel the need to list as a separate benefit, the same largely applies. The type of the commit is completely irrelevant. For the scope, they either don’t know enough about the project to give meaning to the scope you’ve chosen, or they are again interested in the actual scope of the changes. For new contributors, the consequence of less nice commit messages often caused by conventional commits is even worse: they have only the commit messages to understand why a change was made.
Communicating changes to the public is often the task of a changelog, but as discussed, commits are the wrong level for generating those. Commits are intended to record the history (changes and their rationale) of the code. They should not be concerned with communicating changes to the public: if the public wishes, they can consult the commit history directly.
Finally, they are a bit vague about “other stakeholders”, so we can be as well: they again have no business with commits.
The real benefit of conventional commits
Hopefully, by now you are ready to let go of conventional commits, or at least think about why you apply the convention. If you have a good use-case not covered here, feel free to send it to me: I am genuinely interested.
The one benefit of conventional commits in my view is that it is a way to force people to think a bit more about their commits, their change sizes, and their commit messages. If you must classify your commit into a category, you’ll be less likely to just commit every hour and dump changes in commits titled “Changes”.
When working on a project where it is otherwise impossible to get people to think about commits, it might be worth it for that alone. Conventional commits implemented well are better than no useful commits at all. However, after a while, see if the conventional part is still required, or you can just create good commits on their own.
- Although many alternatives do exist. ↩
- Issue № 310 ↩
- Zeng, Q., Zhang, Y., Qiu, Z., & Liu, H. (2025). A First Look at Conventional Commits Classification. In IEEE/ACM 47th International Conference on Software Engineering (pp. 2277–2289) ↩
- If you are squashing your commits, see the previous article why you should not. ↩
- Semantic Versioning Will Not Save You by Hynek Schlawack ↩