Semantic Versioning (SemVer)
You've seen version numbers everywhere: 1.0.0, 2.4.1, 4.0.0-beta.2. But how does it work? How do you know if updating a dependency from 2.1.5 to 3.0.0 will break your entire application?
Semantic Versioning (SemVer) is the answer. It's a universal standard that tells a clear story about what kind of changes are in a new release.
The numbers, Mason! What do they mean?
A SemVer number is broken into three main parts. Once you understand them, you can understand the impact of any version change at a glance.
💥 MAJOR version
The Rule: You increment the MAJOR version when you make incompatible, breaking API changes.
When to use it: A function was removed, an endpoint changed its response, or anything else that would force users of your code to change theirs.
Example: Changing from 1.5.2 to 2.0.0 signals danger. Update with caution!
✨ MINOR version
The Rule: You increment the MINOR version when you add new functionality in a backward-compatible way.
When to use it: You added a new feature, a new optional parameter, or a new endpoint without changing existing ones.
Example: Changing from 2.0.0 to 2.1.0 adds cool new stuff without breaking the old stuff.
🐛 PATCH version
The Rule: You increment the PATCH version when you make backward-compatible bug fixes.
When to use it: You fixed something that was broken but didn't change any features. This is common for hotfixes.
Example: Changing from 2.1.0 to 2.1.1 is a safe and recommended bug fix.
The "Initial Development" Phase
Before your first official release, your MAJOR version should be 0. The version 0.y.z indicates that the project is in its early stages and anything can change at any time. We start with 1.0.0 once the application is deployed to production for the first time.
This way, you don't go to prod for the first time with a v2397.0.0
Anatomy of Our Full Version String
The MAJOR.MINOR.PATCH numbers are just the core. A full version string gives us even more detail.

Our full version string has three parts: f
- Core Version:
MAJOR.MINOR.PATCHas described above. - Pre-release Tag: A label that qualifies the stability of the build. In our workflow, this tag is determined by the branch:
branch-name: Indicates an in-development build from a feature or bugfix branch. Highly unstable.-beta: A build from themainbranch. All features are complete, but it's not yet validated for production.-rc(Release Candidate): A build from asupport/*branch. This is a candidate for a production release and is undergoing final verification.- (no tag): A full production release, tagged and deployed.
- Build Number: An ever-increasing number for each build or commit on a pre-release version, in-between "stable" builds, providing uniqueness.
So, a version like 1.1.0-beta.1 means: "This is the first build of a beta release to prepare for version 1.1.0."
How Versions Are Bumped Automatically
So how do we get the right version number every time?
Our tooling (specifically, a tool like GitVersion) automatically calculates the next version by looking at your commit and the branch you're on. Everytime you will commit, merge, tag, GitVersion will compute the right SemVer for you. You can even use conventional commits to automatically bump the right digit depending on the type of commit.
Example Scenario
Let's see it in action. The last production release was tagged 1.0.0.
- A developer merges a bug fix:
fix(GG-2): my fix.- The version on
mainbecomes1.0.1-beta.1. (Patch bump, first beta build)
- The version on
- Another developer merges a second fix:
fix(GG-3): my second fix.- The version on
mainbecomes1.0.1-beta.2. (Build number increases)
- The version on
- A new feature is merged:
feat(GG-4): my feature.- The version on
mainbecomes1.1.0-beta.3. (Minor bump overrides the patch, build number continues)
- The version on
- Another new feature is merged:
feat(GG-5): my feature.- The version on
mainbecomes1.1.0-beta.4. (Build number increases)
- The version on
- A breaking change is merged:
feat(GG-6)!: new auth system.- The version on
mainbecomes2.0.0-beta.5. (Major bump overrides everything, build number continues)
- The version on
- Finally, we tag the previous commit "2.0.0", and we merge a new feature
feat(GG-7).- The version on
mainbecomes2.1.0-beta.1. (Minor bump because it was afeat-typed commit. Build number got reset with the latest tag).
- The version on
This system ensures our version numbers are always predictable, meaningful, and generated without any manual work. It's the engine that drives the entire workflow.
For all the details, visit the official Semantic Versioning specification.