Semantic Versioning Explained: When to Bump What
Version numbers look like simple counters, but under Semantic Versioning they carry a promise. A change from 2.4.1 to 2.5.0 tells everyone your update is safe to install; a jump to 3.0.0 warns them something will break. Getting the bump wrong can quietly break thousands of builds. This guide explains the rules of SemVer, how pre-releases are ordered, and how package managers turn those numbers into upgrade decisions.
MAJOR.MINOR.PATCH in plain words
A semantic version has three numbers. Given 2.4.1:
- MAJOR (2) โ increase it when you make changes that break existing users, such as removing a function or changing its behavior.
- MINOR (4) โ increase it when you add features in a backward-compatible way.
- PATCH (1) โ increase it for backward-compatible bug fixes.
When one number goes up, those to its right reset to zero: 2.4.1 becomes 2.5.0, not 2.5.1. Versions before 1.0.0 are treated as initial development, where anything may change at any time.
A quick decision guide
- Fixed a typo in an error message? Patch.
- Fixed a bug that people might rely on? Think twice โ it may be a major change for them.
- Added an optional parameter with a sensible default? Minor.
- Renamed a public method or changed a return type? Major.
- Deprecated something but kept it working? Minor, with a warning; remove it in the next major.
- Changed only internal code or documentation? Usually patch, or no release at all.
The key question is always: can someone upgrade without changing their code?
Pre-releases and how they sort
A pre-release adds a hyphen and dot-separated identifiers, such as 3.0.0-alpha.1 or 3.0.0-rc.2. A pre-release always ranks lower than the normal version it leads to. Identifiers are compared one by one: numbers numerically, text alphabetically, and numbers rank below text. That gives this order:
1.0.0-alpha1.0.0-alpha.11.0.0-beta1.0.0-beta.21.0.0-beta.111.0.0-rc.11.0.0
Note that beta.11 comes after beta.2, because the numbers are compared as numbers. Build metadata after a plus sign, as in 1.0.0+build.5, is ignored when ordering.
How to generate test versions with the tool
Code that parses, compares or sorts versions deserves lots of inputs. The generator above helps:
- Leave Include pre-releases on to mix strings like
2.27.45-alpha.2in with stable versions, or switch it off for plain MAJOR.MINOR.PATCH. - Create up to 100 version numbers per batch.
- Paste them into a sorting test and compare your result with the precedence rules above.
- Use them as neutral placeholders in changelog mockups and documentation screenshots.
Version ranges in package managers
Dependency files rarely pin an exact version. In npm-style ranges:
^1.2.3accepts anything from 1.2.3 up to, but not including, 2.0.0.~1.2.3accepts patch updates only: from 1.2.3 up to, but not including, 1.3.0.1.2.3pins exactly that version.
For versions below 1.0.0 the caret is stricter: ^0.2.3 stays below 0.3.0, because minor bumps may break things during initial development. Lock files then record the exact versions installed.
Common mistakes and how to avoid them
Even experienced maintainers trip over the same issues:
- Hiding breaking changes in minor releases. If users must change code, it is a major release, however small the change feels.
- Staying on 0.x forever. A project used in production by many people is effectively stable; releasing 1.0.0 gives them clearer guarantees.
- Forgetting dependencies. Dropping support for an old runtime or platform version is a breaking change for anyone still using it.
- Fear of big numbers. Version 14.0.0 is not a problem. Honest numbers are more useful than small ones.
- Leaving tags out of sync. Make sure the Git tag, the package file and the changelog all show the same version.
Automating releases from structured commit messages removes much of this guesswork, because the tool decides the bump from the changes it sees.
Pro tips
- Write the changelog entry before you choose the bump; it makes the decision obvious.
- Release 1.0.0 as soon as people depend on your public API.
- Never reuse a version number, even for a broken release โ publish a new patch instead.
- Use release candidates for big major versions so users can test early.
- Explain every breaking change in the changelog together with a short migration note, so users know exactly what to update before they upgrade.
Frequently asked questions
Can a version number go above 9?
Yes. Each part is an integer with no upper limit, so 1.10.0 follows 1.9.0 and 4.26.26 is perfectly valid.
Should a version tag start with v?
Many projects tag releases as v1.2.3 in Git. The v is a common convention for tags, while the version itself is just 1.2.3.
What does 0.y.z mean?
It signals initial development. The public API is not considered stable, so users should expect breaking changes even in minor releases.