Repository navigation
Bumping rule is wrongly documented #515
Description
Activity
adam-grant-hendry commented
on May 22, 2022 ContributorAuthorMore actionsHere's what I'm thinking:
I'm good with 1. @woile what do you think?
I think alternatives like the
cz-emojiare good, and it should be added to the Third party templates section (there's already one, but it's not the same as this one).I do think we should update the docs to reflect the current behavior (fix, refactor and perf are patch, not everything).
Regarding option 1, it would be a breaking change. So we should be very careful. I personally have some conflicts with using just feat and fix.
Here some thought to discuss:
- In practical terms, when you use
perf, it means you made a performance improvement and you want it to be released, thus you want to bump somehow your release, right? - If you make a performance improvement, and you only have fix or feat, under which category does it fit? I don't really know, but I know I want it released under PATCH because nothing really changes for the user.
- What do you do in the situations where you don't want to bump the version? I like having a set of "pre-existing" types (build, ci, perf, docs) that do not create a bump when pushing a commit.
I would like to be more compliant with the spec, but the current setup has been working fine for me. Thoughts?
- In practical terms, when you use
adam-grant-hendry commented
on May 22, 2022 ContributorAuthorMore actionsThe most important tasks would be to
- Correct the documentation (state what change types bump versions and that
commitizenincludesAngular Conventiontypes by default) - Add notes in commit message instructions to identify what version bump occurs with all change types (e.g.
Correlates with no version bump in SemVer)
This would match the current (author-intended) behavior of the package.
Here some thought to discuss:
- In practical terms, when you use
perf, it means you made a performance improvement and you want it to be released, thus you want to bump somehow your release, right? - If you make a performance improvement, and you only have fix or feat, under which category does it fit? I don't really know, but I know I want it released under PATCH because nothing really changes for the user.
- What do you do in the situations where you don't want to bump the version? I like having a set of "pre-existing" types (build, ci, perf, docs) that do not create a bump when pushing a commit.
I would like to be more compliant with the spec, but the current setup has been working fine for me. Thoughts?
The purpose of
commitizen(to my knowledge) is- to enforce a consistency in commit messages
- to parse commit messages to determine if versions should be bumped and/or what should go in the changelog
The primary purpose of
Semveris dependency management. The numbers in the version string identify behavioral changes affecting the end user. SinceSemveronly uses 3 (MAJOR, MINOR, PATCH), this is whyConventional Commitsonly identifies 3 (BREAKING CHANGE, feat, fix).To answer your questions:
- The behavior MINOR changes are intended to capture are additions/deletions
- The behavior PATCH changes are intended to capture are modifications to existing items that affect their behavior
Regarding 1 & 2:
A performance improvement could either be a feature or a fix.
- If it is above what the current state of the code offers (makes things faster, uses less memory), it is an addition.
- If it is trying to bring things back to the expected state of the system (e.g. a previous feature/fix degraded the performance of item x/y/z where performance is intended by the author and expected by end users depending on it), then it is a fix.
For this, I use scope to identify the nature of the change so that I as the author can choose whether it should bump MINOR or PATCH, rather than always force it to a particular kind of change:
feat(perf,speed): improve speed of `x` beyond what was originally intended fix(perf,speed): speed up `x` to match what was originally intended (it wasn't meant to be this slow!)I am also of the opinion that documentation should be treated like code and it is perhaps more valuable than your code base (no one will use something with bad documentation or poor/no examples).
- Documentation is outward facing (the end user relies on it; it is a dependency).
- If you fix an error (even typos), then it is a fix and PATCH should bump. If you add a feature to your documentation, it deserves a MINOR bump. Again, I use scope to identify the nature of the change.
- And, as with dependency managers that need to look at version bumps to see whether to include or exclude them, a dependency manager could look at a documentation bump and decide whether or not to include it.
I mention the above because it is confusing that one would have a separate
docstype that bumps nothing, but that in practice you should really be doingfeat(docs)orfix(docs). (Users submit Issues for doc requests and fixes all the time (like me!), so these should be treated like features and fixes linked to the issue).Regarding 3:
This is where a 3rd option, like
refactor, comes into play. The term is literally derived from math:- When you refactor
x^2 + 2x + 1into(x+1)^2, you are not changing the behavior or meaning of the terms. In fact, this is called an identity in mathematics because you can literally replace one term for the other to get the exact same behavior.
Refactors should never bump versions, otherwise they are not refactors. If in refactoring you add something or modify something, then those constitute a MINOR and PATCH bump, respectively.
Conclusion
My practice will be to only use
feat,fix, andrefactortypes (I'm going to eliminatecorrectionfromcz_emojinow (bumpingPATCHfor a typo does not hurt anything, but helps at least identify that a change has occurred):def questions(self) -> Questions: questions: Questions = [ { "name": "prefix", "type": "list", "message": "Select the type of change you are committing", "choices": [ { "value": "🎉 feat", "name": "🎉 feat: (Bumps MINOR) Add/remove an item/feature", }, { "value": "🔨 fix", "name": "🔨 fix: (Bumps PATCH) Fix/modify an existing item/feature", }, { "value": "➗ refactor", "name": "➗ refactor: (Bumps nothing) Reorganizes item(s) without add features or modifying behavior.", }, ], }, ...
ASIDE: I dislike the use of
fixto correspond to abug. Abughas the subconscious connotation that the codebase is broken. Everything may work, but it might not work as intended (either by the author or as depended upon by the end user). I prefer everything have a behavioral connotation, as originally intended bySemver.Limiting choices to 4 (breaking change = MAJOR, addition/removal = MINOR, modification = PATCH, nothing = nothing (refactor)) will make developers think more consciously about the kind of changes they are making. I think things like
doc,build,ci, etc., belong inscopeas they could either be afeator afix(there's no one-size-fits-all).ADDITION: GitHub (and GitLab) offer the ability to post
Releases of your code, and it is a thing independent of your version number. Hence, you can bump versions as often/much as you like and then choose to add a particular version of your code as aRelease. I mention this in case readers think that bumping versions rapidly will fill a repo with artifacts. Issuing a version for release is still the purview of the author/maintainers.- Correct the documentation (state what change types bump versions and that
adam-grant-hendry commented
on May 22, 2022 ContributorAuthorMore actionsThe tl;dr for me is
- version bumps should always be a consequence of a behavioral change to your code
- these should be innate/automatic so there are no hidden changes (accidental or otherwise)
- a version pushed to your outward-facing branch (main/etc.) should have 1 state (i.e. you should not push refactor changes to your main/release branch; those should occur in a development branch before a push to main)
- a Release is separate from a version bump and is a decision made by the author(s) and/or maintainer(s)
- version bumps should always be a consequence of a behavioral change to your code
adam-grant-hendry commented
on May 22, 2022 ContributorAuthorMore actions@Lee-W @woile Would also be good to add to the docs whether configuration files extend existing behavior or completely override it. As it stands, I don't know if I need to specify all items listed in the configuration table or only some.
e.g. After digging through the docs, for customizing through classes, I found
Inherit from BaseCommitizen, and you must define questions and message. The others are optional.
but there are no such instructions for customizing through a config file. I don't know what I must config and what is optional.
adam-grant-hendry commented
on May 22, 2022 ContributorAuthorMore actionsadam-grant-hendry commented
on May 23, 2022 ContributorAuthorMore actionsadam-grant-hendry commented
on May 23, 2022 ContributorAuthorMore actions@Lee-W @woile Lastly, I also want to say "THANK YOU!!!" for this great package! 🎉
Below is my
pyproject.tomlusing standardcommitizen(no emojis) that meets what I've been talking about above (it would be nice to add a section to your docs containing third-party config files, in addition to 3rd-party packages, since not everyone will want to make a package; granted, some things require a plugin and not everything can be done through a config file). Some things worth noting:-
Version
2.20.0does not have thecommit_parseroption, which is necessary forCHANGELOG.mdto be properly rendered. Your docs don't have a version attached to them, so I am only able to see docs for the latest version (important, say, for those reverting to version2.20.0due to Issue Duplicate Changelog Entries for Merged Branches #510 ). So, I bumped back up to2.27.0. -
It would be nice to have an option added that generates a
CHANGELOG.mdheader (withmarkdownlintwithout a header, you get anMD041error. This is what I have (borrowed fromKeep a Changelog):
# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Since this project adheres to [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), the change types here match those in the commit messages: - `Feat` for added/removed/deprecated features - `Fix` for changed/modified items - `Refactor` for changes not affecting behavior
- It would be nice if there was a way to customize a third-party plugin. For instance, it would be nice if I could customize
commitizen_emojiwithout having to create a separate plugin.
My TOML Configuration:
[tool.commitizen] name = "cz_customize" version = "0.2.0" version_files = [ "my_proj/__version__.py", "pyproject.toml:version" ] update_changelog_on_bump = true changelog_start_rev = '0.2.0' annotated_tag = true tag_format = "$version" [tool.commitizen.customize] message_template = "{{change_type}}({{scope}}){% if is_breaking_change == true %}!{% endif %}: {{subject}}{% if body %}\n\n{{body}}{% endif %}{% if footer %}\n\n{% if is_breaking_change == true %}BREAKING CHANGE: {% endif %}{{footer}}{% endif %}" schema_pattern = '(feat|fix|refactor|bump)(\(\S+\))?!?:(\s.*)' bump_pattern = '^(feat|fix)(\(.+\))?(!)?' bump_map = { break = "MAJOR", feat = "MINOR", fix = "PATCH" } change_type_order = ["BREAKING CHANGE", "feat", "fix"] commit_parser = '(?P<change_type>feat|fix|refactor)(?:\((?P<scope>[^()\r\n]*)\)|\()?(?P<breaking>!)?:\s(?P<message>.*)?' changelog_pattern = '^(feat|fix|refactor)?(!)?' [[toml.commitizen.customize.change_type_map]] "feat" = "Feat" "fix" = "Fix" "refactor" = "Refactor" [[tool.commitizen.customize.questions]] name = "change_type" type = "list" message = "Select the type of change you are committing" choices = [ { value = "feat", name = "feat: (Bumps MINOR) Add/remove an item/feature" }, { value = "fix", name = "fix: (Bumps PATCH) Fix/modify an existing item/feature" }, { value = "refactor", name = "refactor: (Bumps nothing) Reorganizes item(s); not a 'feat' or 'fix'" }, ] [[tool.commitizen.customize.questions]] name = "scope" type = "input" message = "Scope. Enter the scope of the change, category first (docs/test/ci/build/perf), followed by class or file name if applicable (comma-separted, no spaces):\n" [[tool.commitizen.customize.questions]] name = "subject" type = "input" message = "Subject. Enter the short summary of the change (imperative tone, lowercase, no period):\n" [[tool.commitizen.customize.questions]] name = "is_breaking_change" type = "confirm" message = "Is this a BREAKING CHANGE (backwards incompatible)? (Bumps MAJOR; default: N):\n" default = false [[tool.commitizen.customize.questions]] name = "body" type = "input" message = "Body. Enter complete details about the change (use full sentences with proper grammar): (press [enter] to skip):\n" [[tool.commitizen.customize.questions]] name = "footer" type = "input" message = "Footer. Reference any Issues this change addresses. If a BREAKING CHANGE, enter details. (press [enter] to skip):\n"
-
adam-grant-hendry commented
on Oct 23, 2022 ContributorAuthorMore actions@woile @Lee-W I've had some time to investigate and practice with
semverand found some additional articles that have changed my opinions a bit.The following are several great articles related to semantic versioning (perhaps well-known by now):
- Semantic Versioning Will Not Save You, by Hynek Schlawack
- Why I don't like SemVer anymore, by Brett Cannon
- Versioning Software, by Donald Stufft
These articles opened my eyes to Hyrum's Law, which indicates you cannot guarantee a priori what changes will break end-user code. You can only guarantee what changes will break your API (i.e. how you claim your library is intended to be used) up to the tests contained within your test suite. This has significant implications for dependency managers (like
poetry) that implicitly rely onsemverbeing a reliable outward-facing contract to end-users to identify what will break their code.The above has been a source of tension in the community:
- e.g.,
poetrybinds major versions, which was quite a divisive choice on the developers' part (consequences of pinning major versions is explained in Hynek's article). - 0ver has become a popular versioning strategy for several libraries
- PEP 440 supports the 0ver schema, as Brett mentions
- CalVer is also another popularly used alternative
The comment I like the most is Brett Cannon's epiphany:
version numbers are just a mapping of a sequence of digits to our branching strategy in source control
What is understated here is that the community implicitly understands major versions to be long-lived branches. When a
v1is released, the implicit statement to end users is:- The current API is stable enough and relied upon by a sufficient number of users to warrant long-term maintenance
- The maintainers will submit patches, bug fixes, hot fixes, etc., for this branch
- There is an implicit release schedule, beyond which this branch will be sunset/deprecated and support will no longer be available
I believe the
commitizendocumentation should make explicit thatsemvercannot guarantee whether or not a different version of installed software will break end-user code, but the notions ofBREAKING,feat,fix, etc., should be understood within the context of the library by itself, without other libraries, as tested by the project test suite.For example, this is how library maintainers should treat the following and understand their end-user contract to be:
"Any change made to this library may break end-user code at any time. We can only guarantee which changes are breaking and non-breaking solely within the context of the usage of this library by itself, as documented in the API, both up to and limited by the comprehensiveness of this library's test suite."
feat: We have added a feature to this library that previously did not exist. We have tested it and it does not break our API, but it may break your code.fix: We have fixed something that did not work as prescribed in our API. We have tested it and it does not break the original intended usage of our API, but it may break how you're using it.BREAKING: We have either added a feature or fixed something that alters the original intended usage of the API. This may or may not break your code.
Intended as used above means "what is/was intended by the developer(s)" and any change (
feat,fix,BREAKING, or otherwise) may or may not break your code.Donald states this well:
At the end of the day, the most important, but often overlooked, aspect of the version is that one of it’s primary purposes is to communicate with the end users of the software. It’s up to the project what information is most important to their end users that it deserves to be communicated through the version number, though I would suggest that for most projects, SemVer-ish is likely to be best suited.
Additional Excellent Points
In almost 20 years of professional software development I have observed that the amount of unintentional breakage through updates outweighs the amount of intentional breakage by far.
Reacted by Wei Lee and gebbissimoI kinda miss the context... I 'll try to take a look at later point
@Lee-W what is the status of this issue?
btw the comment from @adam-grant-hendry may greatly help users understand how versioning works
we can consider adding those to our document as recommended reading materials
I quickly went through it. Definitely a good thing to add to the doc!
- changed the title
[-]Content to be added or fixed[/-][+]Bumping rule is wrongly documented [/+]on Nov 30, 2025 - changed the title
[-]Bumping rule is wrongly documented [/-][+]Bumping rule is wrongly documented[/+]on Nov 30, 2025 - added a commit that references this issue
on Feb 20, 2026 - added 2 commits that reference this issue
on May 9, 2026
Type
URL
https://commitizen-tools.github.io/commitizen/bump/
Description
Problem
The docs state "
fix+everything else" bumpsPATCHof the version string per the defaultcommit_map.However, this is not the case:
commitizen/commitizen/defaults.py
Lines 78 to 80 in 7916511
Only
fix,refactor, andperfbumpPATCH, leaving outdocs,style,test,build, andci, which don't bump any part of the version.Not only are the docs incorrect, but this is out of step with Conventional Commits 1.0.0. Additional commit types are permitted by the specification (e.g.
commitizenappears to be using the Angular Convention by default), but no guidance on version bumping is given for these addition types. Where a commit type bumps or does not bump a version, this should be explicitly stated (e.g. the commit message instructions forfixhasCorrelates with PATCH in SemVer, but notrefactororperf, leaving one to believe these do nothing).Recommendations:
(PREFERRED; Conventional Commit spec): Only
featureandfixshould be in the defaultcommit_mapper Conventional Commits 1.0.0.refactorandperfshould not bumpPATCH. The Angular typesrefactor,perf,docs,style,test,build, andcishould be removed from the default commit types.(ALTERNATIVE; Angular Convention, but with explicit messaging):
refactorandperfshould not bumpPATCH. The commit message instructions forrefactor,perf,docs,style,test,build, andcishould addCorrelates with no version bump in SemVer. In addition, the docs should clearly state the Angular Convention commit types are included incz cby default.Tasks: