Developer Tools
Semver Comparator - Compare Semantic Version Numbers
Compare two semantic version strings (1.2.3, v2.0.0-beta.1) to find which is greater, sort a list of versions, or calculate the next bump.
| Part | A (1.4.0) | B (2.0.0-beta.1) |
|---|---|---|
| major | 1 | 2 |
| minor | 4 | 0 |
| patch | 0 | 0 |
| Pre-release | - | beta.1 |
| Build meta | - | - |
Next bumps from A (1.4.0)
What is Semantic Versioning?
Semantic Versioning (SemVer) defines a version number as MAJOR.MINOR.PATCH:
- MAJOR: Incompatible API changes. Users must update their code.
- MINOR: New backward-compatible features.
- PATCH: Backward-compatible bug fixes.
An optional pre-release label (-alpha.1, -beta.2, -rc.1) is appended after the patch. Build metadata (+sha.abc) is appended last and is
ignored in comparisons.
Comparison rules
Versions are compared left-to-right: major, then minor, then patch. If all three match,
pre-release fields are compared field-by-field, split by .. Numeric fields are
compared numerically; alphanumeric fields are compared lexicographically. A release version
always beats its pre-release counterpart.
Common version ranges (npm / semver)
^1.2.3: Compatible with 1.2.3 (same major)~1.2.3: Approximately 1.2.3 (same major.minor)>=1.2.3 <2.0.0: Explicit range
Pre-release ordering pitfalls
Pre-release version ordering follows specific rules that often surprise developers. Here is the correct order for a single major version:
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0
Key rule: a numeric pre-release identifier compares numerically (beta.2 < beta.11), while an alphanumeric identifier compares lexicographically. A numeric
field also sorts before an alphanumeric field at the same position - which is why
alpha.1 precedes alpha.beta.
Package manager range differences
The same range expression can mean different things across package ecosystems:
| Range | npm | Cargo (Rust) | Composer (PHP) |
|---|---|---|---|
~1.2.3 | ≥1.2.3 <1.3.0 (patch-level) | ≥1.2.3 <1.3.0 (same) | ≥1.2.3 <1.3.0 (same) |
^1.2.3 | ≥1.2.3 <2.0.0 (compatible) | ≥1.2.3 <2.0.0 (same) | Not supported natively |
~0.2.3 | ≥0.2.3 <0.3.0 | ≥0.2.3 <0.3.0 | ≥0.2.3 <0.3.0 |
^0.2.3 | ≥0.2.3 <0.3.0 (special 0.x rule) | ≥0.2.3 <0.3.0 (same) | Not supported natively |
Why SemVer is often not followed
In practice, many libraries increment MINOR for changes that break some users' code when the authors feel the change is "small enough" or unlikely to affect real-world usage. Breaking changes sometimes ship as PATCH releases with the justification that the old behavior was a bug. SemVer is fundamentally a social contract between maintainers and consumers - it only works when both parties agree on what "breaking" means. When adopting a dependency, always review the changelog rather than relying solely on the version number.