Semantic Versioning (semver) assigns specific meaning to each of the three numbers in a version string: major increments on breaking changes, minor increments on backward-compatible feature additions, and patch increments on backward-compatible bug fixes. This isn't just convention for its own sake — dependency managers like npm's caret (^) and tilde (~) ranges are built entirely on the assumption that a package's maintainer actually follows these rules, so a package that bumps only its patch version while quietly introducing a breaking change violates the contract every downstream consumer's version range is relying on.
Prerelease and build-metadata suffixes carry their own precedence rules that surprise people. 1.0.0-alpha sorts before 1.0.0-alpha.1, which sorts before 1.0.0-beta, which sorts before the plain release 1.0.0 — prereleases always sort lower than the release they precede, regardless of how far along the label sounds. Build metadata (the +build.20240115 suffix) is explicitly excluded from precedence comparisons entirely per the spec — two versions differing only in build metadata are considered equal for ordering purposes, which trips people up when they expect it to act as a tiebreaker.
The most consequential number is the major version, specifically when it's 0 — semver explicitly designates 0.x.y as 'anything may change at any time,' meaning the normal breaking-change-requires-major-bump rule doesn't formally apply yet. A huge number of real-world packages stay on 0.x for years, which means every minor bump in that range can legitimately be breaking — something dependency ranges like ^0.4.2 handle very differently from ^4.2.0 (the former only allows patch bumps, the latter allows minor bumps too), a distinction that's easy to miss when skimming a package.json.
This parser breaks any semver string into its five components, explains the precedence rules that apply to the specific prerelease and build-metadata present, and — when given two versions or a version and a range — explains what the comparison means for compatibility under standard caret and tilde semantics.
Edge cases worth knowing
- A 0.x.y version bumping its minor number
- Per the semver spec, major version zero is explicitly for initial development, where anything may change. A jump from 0.3.0 to 0.4.0 can be a breaking change even though a jump from 1.3.0 to 1.4.0 (with the same numeric bump) must not be — this parser calls out the 0.x special case explicitly rather than treating all minor bumps identically.
- Two versions differing only in build metadata
- 1.2.3+build1 and 1.2.3+build2 are defined by the semver spec as having equal precedence — build metadata is informational only and must be ignored when determining version order. This parser reports them as equal for comparison purposes even though the strings aren't identical.
- A version string with a leading 'v', like v2.4.1
- The leading v is an extremely common convention (particularly in git tags) but isn't part of the formal semver grammar. This parser strips it before parsing rather than rejecting an otherwise valid version string.
Common mistakes
- Bumping only the patch version for a change that removes or renames a public API, breaking every consumer relying on strict patch-only version ranges.
- Assuming a longer or more official-sounding prerelease label (rc.1 versus beta.1) automatically sorts higher — precedence is determined by the semver comparison algorithm, not by how advanced the label sounds.
- Treating a 0.x.y package as version-range-safe under normal caret semantics, when 0.x explicitly opts out of the stability guarantees those ranges assume.
- Comparing two versions by build metadata when it should be ignored entirely for precedence per the spec.