SemVer’s MAJOR.MINOR.PATCH format looks trivial, but getting it wrong breaks dependent projects in ways that erode trust fast. Understanding what each number actually promises matters whether you’re publishing a package or just deciding how to tag your next release.
What Each Number Means
- MAJOR — incremented for breaking changes. Anything that could make existing code stop working.
- MINOR — incremented for new, backward-compatible functionality.
- PATCH — incremented for backward-compatible bug fixes only.
1.0.0 -> 1.0.1 # bug fix, no API changes 1.0.1 -> 1.1.0 # new feature added, old code still works unchanged 1.1.0 -> 2.0.0 # breaking change — old code may need updates
Version Ranges in package.json
{
"dependencies": {
"express": "^4.18.0",
"lodash": "~4.17.21",
"react": "18.2.0"
}
}
^4.18.0 allows any 4.x.x update (minor and patch), ~4.17.21 allows only patch updates within 4.17.x, and a bare 18.2.0 pins the exact version with no automatic updates at all.
The Trust Contract
SemVer is fundamentally a promise to your consumers: “if you only pull minor and patch updates, your code will keep working.” Breaking that promise — shipping a breaking change as a patch release — is how dependency updates end up silently breaking production for everyone downstream, and why teams eventually stop trusting your version numbers at all.
Pre-release and Build Metadata
2.0.0-alpha.1 # pre-release, sorts before 2.0.0 2.0.0-beta.3 2.0.0-rc.1 # release candidate 2.0.0 # the actual stable release