Semantic Versioning (SemVer) Explained
Basics3 minInnovialabs editorial team
In short
SemVer is the MAJOR.MINOR.PATCH format, where MAJOR grows on incompatible changes, MINOR on backward-compatible features and PATCH on bug fixes; a lock file pins the exact dependency versions.
SemVer in short
Semantic versioning is an agreement that a version number tells you how risky an update is. The format is MAJOR.MINOR.PATCH, for example 2.5.1:
| Part | When it increases | What it means for users |
|---|---|---|
| MAJOR | incompatible public API changes | the update may break your code |
| MINOR | new backward-compatible functionality | safe to update, old code keeps working |
| PATCH | bug fixes without API changes | a safe update |
When a higher part increases, the lower ones reset to zero: 1.4.7 → 2.0.0, 1.4.7 → 1.5.0.
The key concept is the public API. SemVer only makes sense when it is clearly defined what counts as the "interface": library functions, endpoints, configuration format. Changing internal code that nobody calls from outside does not break compatibility.
Special cases
- Versions
0.x.ymean initial development. According to the spec anything may change at this stage, and no API stability is promised. 1.0.0is the moment you declare the public API stable.- Pre-release is a hyphen suffix:
2.0.0-alpha.1,2.0.0-beta.3,2.0.0-rc.1. Such a version ranks lower than the final2.0.0and promises no stability. - Build metadata is a plus suffix:
1.0.0+20261010. It does not affect version order.
Pre-release ordering: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta < 1.0.0-rc.1 < 1.0.0.
The ^ and ~ ranges in package managers
In package.json (and its equivalents in other ecosystems) a dependency is often declared as a range rather than an exact version:
| Notation | What is allowed | Example for 1.4.2 |
|---|---|---|
1.4.2 | this version only | 1.4.2 |
~1.4.2 | PATCH updates | >=1.4.2 <1.5.0 |
^1.4.2 | MINOR and PATCH updates | >=1.4.2 <2.0.0 |
* or latest | anything | not recommended |
An important npm detail: below 1.0.0 the ^ sign is more cautious. ^0.3.1 only allows >=0.3.1 <0.4.0, because in major version zero MINOR effectively plays the role of MAJOR.
The exact range rules are in the npm documentation: semver ranges.
Lock files: protection against "it worked yesterday"
The range ^1.4.2 means a fresh install may give you 1.9.0. If the library author accidentally broke compatibility in a MINOR release, your project fails without a single change in your code.
A lock file (package-lock.json, yarn.lock, pnpm-lock.yaml, poetry.lock, composer.lock) records the exact versions of the whole dependency tree, including transitive ones. What matters in practice:
- Commit the lock file to the repository for applications.
- Use a strict install in CI:
npm ciinstead ofnpm install— it installs exactly what is in the lock file and fails if it does not matchpackage.json. - Update dependencies deliberately: as a separate task, with tests and a read of the changelog for MAJOR versions.
- Automated update tools such as Dependabot or Renovate open separate pull requests — convenient when the project has tests.
How to version your own project
- Define what your public API is and document it.
- Keep a CHANGELOG: what was added, fixed and what breaks compatibility.
- Remove features in two steps: first mark them deprecated in a MINOR release, then remove them in the next MAJOR.
- Do not be afraid of MAJOR releases: an honest number beats a "silent" break in a PATCH.
- For websites and internal apps without external consumers strict SemVer is optional — a date or build number is often enough.