I want my projects to be SemVer compliant. I follow a trunk-based development approach. I also want to keep things as simple as possible.

I assume that each commit on main (default branch) is a possible release candidate. So my CI/CD pipeline looks at the M.m.p version number in the codebase, and build & release artefacts (e.g. npm/Python packages, Docker images) with a M.m.p.rc<commit_sha> tag (e.g. 3.2.0.rc7e97e56).

A “release” is simply a commit on main which has been tagged (patch versions dealt with a bit differently). The changelog should be updated only once per release.

Question: When should the M.m.p version be bumped? On releases, or right after?

My ulterior motivation is to have it eventually all automated via release-it.

Approach 1: Bump on release commits

A release consists in one single commit that updates the changelog and bump the version to the value of the version/tag (example here).

enter image description here

  • pros: one single commit to bump version + release.
  • cons: “release candidate” versions don't make much sense, as 0.1.0.rc1 happens after 0.1.0 has been released. (That's a blocker for me.)

Approach 2: Bump right after release

Right after a release, add a new tag which bumps the default version on main. Release commit is simply about updating the changelog.

enter image description here

  • pros: “.rc” versions now make sense,
  • cons: a lot of extra clutter commits.

Approach 3: Set version on release commits

In this approach, the version in main remains 0.0.0 and releases are done on specific branches, where the release commits update the changelog and set the version to the correct value (example here).

enter image description here

(note that the picture is misleading, as it doesn't show the separate branch.)

  • pros: one-commit release,
  • cons: un-informative version number for main's “.rc” builds.
  • Note that my policy is that anything released from main, no matter how small the change is, is a minor release. (Patch releases are only in case I want to keep supporting a non-latest version and apply a hotfix to it — so not pure SemVer after all.) So it's easy to know what version to bump in the “bump-only commits”: it's M.m+1.0 on main and M.m.p+1 on release branches.
    – ebosi
    Jul 13, 2023 at 12:41
  • Where is this version number stored? In a config file? Is this file tracked in version control? Jul 13, 2023 at 18:19
  • 1
    Why do you need release candidate versions? It seems simpler just to version everything from main/trunk using the same format (without rc in the version) and then use a separate 'publish' step to only promote those versions which you want shipped upto the production artefact registry. (Typically involves using another separate private/internal registry for all the artefacts produced by the CI/build process, to make those available internally). Jul 13, 2023 at 19:03
  • @GregBurghardt Yes, the version is stored in .toml or .json files, tracked in the repo.
    – ebosi
    Jul 14, 2023 at 6:56
  • 1
    @ebosi - my suggestion would be to use a different internal, private registry for internal matters such as testing. That removes versioning from the release process and makes it solely a build concern. The release process can just be about publishing to the external registries instead Jul 14, 2023 at 13:46

2 Answers 2


When I think about this the first thing that comes to mind when I think about your question, is that the version should be bumped according to specific rules. and this is pretty much the way I do it and still follow SemVer:

  • Major Version (M): Increment the major version when you make incompatible changes to the public API or introduce significant new features. This indicates that the new version may not be backward compatible with previous versions.

  • Minor Version (m): Increment the minor version when you add functionality in a backward-compatible manner. This means that the new version introduces new features or enhancements but does not break existing functionality.

  • Patch Version (p): Increment the patch version when you make backward-compatible bug fixes or patches. This means that the new version addresses issues or fixes bugs without introducing new features or breaking existing functionality.

This might not be the best way but from what you described, and this has worked for me, so although you use trunk based, I think this will work for you. I do each one pretty much immediately after the type of whatever change that I am doing that fit the Mmp context.

  • To make sure I understand, right after a PR has been [squash-]merged, you add another commit that bumps the version according to the type of change it was? Or do you change the version in the PR itself?
    – ebosi
    Jul 15, 2023 at 8:17

The way I see it you are doing a few things "wrong"

  1. Confusing versions of code and versions of your product

    Versioning code is problematic, I can have concurrent branches, rollbacks, delayed merges etc. Normally you just verison your product, the compiled or packaged code.

  2. The "trunk based dev" you link to has release branches and feature branches, but you seem to be trying to get everything to work on a single branch.

If you "released" your product in some way that wasn't just a commit, such as publishing it to an ftp site, or compiling it or something. You would have the opportunity at that stage to set the version number, of the product as opposed to the version of the code You could then simply note in the release notes that it was built from commit x and not version the code at all.

If you used release branches you could use the initial branch commit to set the version on that branch.

The tools are there for you to solve this, It's your self imposed limits which are causing your problems.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.