Skip to content
Toolcroft

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.

1.4.0 < 2.0.0-beta.1
12
majorchanged
.
40
minorchanged
.
00
patch
PartA (1.4.0)B (2.0.0-beta.1)
major12
minor40
patch00
Pre-release-beta.1
Build meta--

Next bumps from A (1.4.0)

major: 2.0.0minor: 1.5.0patch: 1.4.1

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:

RangenpmCargo (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.