mirror of
https://github.com/conventional-commits/conventionalcommits.org.git
synced 2026-03-22 12:44:37 -05:00
Proposal: Clearer definitions for Conventional Commit types #219
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @0x404 on GitHub (Jan 21, 2025).
We conducted a study and found that conventional commits are becoming increasingly popular among top open-source projects. Specifically, there is a stable uptrend in the adoption of conventional commits, with some projects mandating their use. By 2023, in repositories that do not require conventional commits, we found nearly 10% of commits were in line with the Conventional Commit format, indicating their popularity among developers.
During this process, we noticed that some commits, despite being conventional, clearly misused the commit message format, for example: feat: remove unnecessary lines from test file. This spurred our curiosity about the challenges developers face when categorizing commits using the conventional commits format. Therefore, we analyzed all the issues in this project, as well as the top 100 questions on Stack Overflow related to conventional commits. Our analysis identified four categories of challenges developers face, with the most common (around 57.7%) being confusion over which type to use. In the current CCS definition, categories other than 'feat' and 'fix' lack clear definitions, leading developers to rely on the categorizations used by Angular and other projects, which often overlap and lack clarity. For instance, in Angular's definition, 'refactor' is defined as "A code change that neither fixes a bug nor adds a feature," which could ambiguously include 'style', 'perf', 'test', among others.
We have proposed a clearer and less overlapping list of definitions based on our literature review and the documentation in repositories currently using conventional commits. Based on this, we have drafted an academic paper (you can find more detailed information about the above here), which has undergone peer review and been accepted by ICSE 2025, a premier software engineering conference.
We are happy to provide additional details or clarification about our methodology and findings. We look forward to any feedback you may have.
@Aseem007-007 commented on GitHub (Feb 13, 2025):
Pal
@bcoe commented on GitHub (May 5, 2025):
@0x404 this is super interesting. Apologies that I'm a terrible open source maintainer these days, and am responding months after the fact.
If you would like to draft a recommendation for a better set of recommended types, I'm very open to the discussion. As long as we maintain the goal of simplicity and minimalism.
Personally, I think less is more in terms of categories, preferring to almost always use
feat:,fix:,docs:,fix(deps):(for a dependency update).@0x404 commented on GitHub (Jun 6, 2025):
Hi @bcoe, my sincere apologies for the late update, just been through an exceptionally busy month.
I would absolutely love to draft a definition list for conventional commit types. Below are the definitions we proposed in our paper:
.travis.yml'' and.github/workflows''.I completely agree that less is more when it comes to categories, but I believe having clear definitions helps users understand each type better. The current Conventional Commits 1.0 specification redirects to projects like Angular, but Angular's definitions are quite ambiguous with overlap. For example, their "refactor" definition ("A code change that neither fixes a bug nor adds a feature") is problematic.
I think we could streamline these proposed definitions while maintaining clarity to help users better understand each type. I'd greatly appreciate any insights you might have, and I'm available to collaborate on this, I'll respond as promptly as possible.
@Aseem007-007 commented on GitHub (Jun 6, 2025):
(null) - مفتاح وصول جهة الاتصال الوارثة 2.pdf
@stechio commented on GitHub (Jun 14, 2025):
As it's used to say, a picture is worth a thousand words... 😄
This diagram represents the orthogonal relation between source code categories (black boxes, upper-left labels) and Conventional Commits types (colored boxes, lower-right labels).
Related commit types share the same color:
EDITS:
BREAKING CHANGEcommit subset has been expanded to encompass all possible casesfeat,fixandstylecommit types have been expanded to encompass all possible cases@JohnnyWalkerDigital commented on GitHub (Dec 16, 2025):
There is a dire need for clear and clean definitions for each of the prefixes. It's kind of astonishing that the specification doesn't already have them.
@edenworky commented on GitHub (Dec 20, 2025):
I'll go a step further, building on the lovely picture above, and say that to organise commits by file type is pretty silly, really. As an example I ran into trying to adapt conventional commits: I write and update my tests and my code in tandem; a single update is reflected across both code and tests files and therefore (in my eyes) belong in a single commit, in which case I use a more appropriate type than
test:. Likewise, if I change tests around or write a couple extra tests without changing the functionality of the code, that's morerefactor:to me, and so I ended up never usingtest:for anything.This is perhaps a somewhat belabored example, but not too uncommon I'm sure. In any case, commits surely are unified named units of changes across one or many files of one or many types, and if (and where) the types are restrictive of that, they'll either be unused or be used at the expense of the actual
git log.A potential solution might be supporting multiple types (messy, cluttered), and a better one might be a sort of hierarchy of types (i.e., "use
test:if your commit contains tests" but with a "lower priority" than "usefeat:if your commit adds a feature or changes functionality", which naturally should include tests).This is not to say that everyone works or should work like this, just that I do and I know other who do, and that the current spec doesn't really address this.
@stechio commented on GitHub (Dec 27, 2025):
@edenworky:
The spec does NOT organize commits "by file type": they are organized by purpose, hence their orthogonality to source code categories. Nonetheless, there's indisputably a certain degree of semantic ambiguity in the spec which gives room to conflicting interpretations; however, after wrestling a bit to match real-world cases and respective commit types, implicit patterns tend to emerge (and should desirably be made explicit in future revisions of the spec). In particular, the answer to the spec FAQ "What do I do if the commit conforms to more than one of the commit types?" may sound somewhat misleading (emphasis is mine):
The key here is "whenever possible": what the spec implies is that a commit should contain nothing but a cohesive set of modifications (like your first case, with production code and related tests); if such cohesive set of modifications spans multiple commit types (in your case,
feat/fixfor production code andtestfor related tests), the actual type of the commit is the most relevant one (which corresponds to your concept of commit type hierarchy — in your case,feat/fixwill mark a single commit encompassing both production code and related test modifications). Conversely, your second case (a couple extra tests without changing the functionality of the production code) will be marked withtestcommit type, sincerefactoris specific to production code (as clearly shown in my diagram here above — its topology is significant, pay attention, please!). That's because production code, being at the core of a project and driving functions like semantic version bumping, enjoys the maximum granularity, whilst test code, being ancillary to a project, hastestcommit type encompassing ALL kinds of changes, except source formatting (which is marked bystylecommit type) and documentation (which is marked bydocscommit type) .Sometimes even a cohesive set of modifications may require to be split into separate commits, though: for example, if a modification in the build configuration causes the code formatting to change, it is more appropriate to commit the build configuration change first (with
buildcommit type), then the affected code (withstylecommit type) — funneling both build configuration and affected code together into a single commit would bury the actual modification (build configuration) under tons of derivative code changes!Recapping:
I agree that the spec should indeed explicitly state these prescriptions, which currently are only subtly implied.
@edenworky commented on GitHub (Dec 27, 2025):
Hi, thanks for the detailed and enlightening response.
You're right, I totally missed that
refactor:doesn't span over test code likestyle:does. I also note thatperf:(performance?) is likewise limited only to production code.Your prescriptions sound like a more refined version of what I naively intuited in my last post, and I agree they should be clear and explicit in the spec, and I'd go as far as to say that the FAQ you quoted is absolutely misleading in this regard, at least in lack of said prescriptions laid out with greater priority. If the answer to the question "What do I do if the commit conforms to [both
fixandtest]?" is to "go back and make multiple commits if possible", then in this case it's certainly possible, it would just lead to redundant commits and a messier log, if one were to act as prescribed in the spec currently.Your prescriptions make much more sense to me, i.e. "if [one cohesive unit of diffs] fits both
fixandtestthen it's one commit of typefix" makes much more sense (i.e., if I review a commit of typefix, I'd expect to see a production code change and a test (or a couple) reflecting it), but it's entirely possible to break it up into multiple commits, and that's what's explicitly prescribed by the FAQ you quoted.So, agreed entirely. Maybe a little decision tree graph could show this clearly and simply (covering your recap essentially), and amending the FAQ; really the question being answered there is "what do I do if I'm trying to commit more than one cohesive set of modifications in one commit", which should be avoided, but it doesn't answer "what do I do if my one cohesive set of modifications fits more than one type", which is what it sounds like it's answering, and answering by "split it into multiple commits if at all possible", which I understand now is not the intent but that's really what it reads as.
Circling back to my earlier point about
refactorandperfnot applying totestin the graph you shared.My particular needs are perhaps somewhat unconventional in this regard, but not terribly so.
I sometimes work with proof systems, and I often employ generative and/or property-based testing, as well as "golden"/snapshot testing. I've also had medium+ webdriver suites. In all these cases as well as many others (e.g. test DB seeding), both performance and code structure/factoring (e.g. page models, custom assertions, data/code generation, etc etc) matter a great deal, and I'll often refactor/optimise my tests (as well as the occasional
fix). Perhaps in this casetestis more scope than type? But certainly I've used these types for cohesive modification sets which were only over test files plus/minus some invocation change or smth. Certainly it's not aciconcern (running tests faster is moreso a dev env concern than a CI/CD concern tho it helps certainly). What should be prescribed in these cases?@stechio commented on GitHub (Dec 28, 2025):
@edenworky:
If you agree with my points, please apply a reaction emoji to my comment, so it better sticks out as a contribution to the topic.
@edenworky:
First of all, a disclaimer: I'm NOT an author of the spec, so I'm NOT in a position to dictate what should be prescribed (the "prescriptions" I formulated in my previous comment are just an inference combining the spec with heuristics).
Going back to your question, you should ditch all your assumptions and simply fit your case into the spec: if you keep resisting and bending the spec to fit your assumptions, you are going nowhere, as the purpose of a spec is to provide a basic set of rules everyone agrees upon, NOT to push your assumptions onto others 😉 — so:
test, in ALL cases!refactorandperf). In this case, you could employ "test" as scope, BUT that would be totally arbitrary and unrelated to the spec@stechio commented on GitHub (Dec 28, 2025):
Reading the proposal definitions, I noted some details to amend.
@0x404:
TO IMPROVE: I'd replace "codebase" with "production code", as the former is ambiguous (a codebase includes also test code, build configuration, CI/CD configuration). Furthermore,
fixshould include also updates to production dependencies.@0x404:
TO IMPROVE: I'd clarify
perfandrefactorapply to production code only (test code is always associated totestcommit type).@0x404:
WRONG: you should think about dependencies as parts of the codebase delegated externally: updating production dependencies belongs to commit type
fix(as already stated by @bcoe); on the other hand, updating build dependencies, which have no use outside the build process of the project, belongs to commit typebuild.@javier-godoy commented on GitHub (Dec 29, 2025):
@edenworky @stechio
In our team's guidelines, we interpret Conventional Commits as a framework that encourages "logically atomic commits". The goal is for every commit to be a self-contained unit: valuable on its own, yet small enough to review or revert∗ without friction.
∗ While reverting isn't the expected action, we use "revertibility" as a helpful heuristic: if a revert would undo unrelated logic or too much context, the commit was likely too large; and if it leaves the codebase in an inconsistent or broken state, the commit was likely too small. It’s a common-sense way to gauge atomicity without relying on rigid rules.
More about atomic/logical commits:
@JohnnyWalkerDigital commented on GitHub (Dec 29, 2025):
Logically atomic commits are a great idea... Serious question: what do you do if you make a mistake and commit something only to discover it fails somewhere, and so the feature isn't complete? Do you make a new commit (
fix:-- even though the feature was never even completed) or do you revert your commit until it's at least working?Side note: IHMO tests and features/refactors are intrinsically linked. Adding a new feature should, in turn, mean news tests are added (especially if you're doing TDD). And getting bogged down in ensuring that no tests were updated as part of a refactoring or created as part of a new feature seems headache-inducing to me.
If only tests were updated, I'll use
test, but if a new feature is added, along with accompanying tests, I'll place it underfeat. Likewise if a refactor results in tests updating, I'll userefactor.@javier-godoy commented on GitHub (Dec 30, 2025):
@JohnnyWalkerDigital
If we find and correct a bug before merging the feature branch, then we amend/squash the original
feat:commit to ensure it remains atomic. However, once the branch has been merged into the main history, we use a separatefix:commit. Personally, I view these post-merge adjustments (so long as they occur before a release) as "incremental changes", which I regard as a different beast than afix:, though most of my colleagues disagree with that distinction.@stechio commented on GitHub (Dec 31, 2025):
@JohnnyWalkerDigital:
We have already discussed these cases a couple of comments above, here and here, no need to repeat all over again 😉
@makesnosense commented on GitHub (Jan 31, 2026):
@0x404 thank you for interesting research on the topic. Your finding that 57.7% of confusion stems from type selection closely matches my experience.
My biggest pain-point is categorizing commits that improve existing code without fixing bugs or adding features. Examples:
The beta spec had
improvementfor exactly this case ("commits that improve a current implementation without adding a new feature or fixing a bug"), but it was removed. Usingstylecreates ambiguity with code formatting, andrefactorimplies structural changes.What's the current recommended approach for these incremental quality improvements?