Code & Dev

Free Semantic Version Comparator Online

Compare two semver version strings. Check if one is greater, equal or less. Validate semver format.

What is semantic versioning and why was it created?

Semantic Versioning (semver) is a formal specification for version numbers that encodes meaning about the nature of changes between releases. It was created by Tom Preston-Werner, co-founder of GitHub, to solve a fundamental problem in software dependency management: "dependency hell" — the situation where upgrading one package breaks another because version numbers carry no reliable information about compatibility.

Before semver, version numbers were largely arbitrary. One library might use dates (20240115), another sequential integers (42), another internal codenames (Bionic Beaver). Package managers could not reason about compatibility, so developers either pinned to exact versions (missing security fixes) or used wildcards (risking breakage from major changes).

Semver's insight is simple but powerful: version numbers are a communication contract between library authors and their users. A version bump from 1.2.3 to 1.3.0 explicitly promises backward compatibility. A bump to 2.0.0 explicitly warns of breaking changes. This shared language allows package managers to automatically determine safe upgrade ranges.

The semver specification — MAJOR.MINOR.PATCH rules

A semver version number has exactly three numeric components separated by dots. Each component has a precise rule for when it must be incremented:

MAJORIncrement when you make incompatible API changes
  • Removing a public function or method
  • Changing a function signature (adding required parameters)
  • Changing the return type of a function
  • Renaming a public class or module
  • Changing behavior that existing code depends on

When major is incremented, minor and patch are reset to zero. 1.4.7 → 2.0.0. Major version 0 (0.y.z) is special: anything may change at any time.

MINORIncrement when you add backward-compatible functionality
  • Adding new public functions or methods
  • Adding new optional parameters with defaults
  • Adding new endpoints to an API
  • Adding new config options
  • Deprecating functionality (but not removing it)

When minor is incremented, patch resets to zero. 1.4.7 → 1.5.0. Existing code continues to work without modification.

PATCHIncrement when you make backward-compatible bug fixes
  • Fixing a calculation error
  • Fixing a crash or null pointer exception
  • Fixing a security vulnerability
  • Improving performance without changing the API
  • Fixing incorrect documentation

Patch fixes must not change public API behavior in any observable way. If the fix requires an API change, it is a minor or major bump.

Pre-release versions and build metadata

Semver supports two optional extensions beyond the three-part version number. Both are appended to the base version:

Pre-release identifiers (- prefix)
1.0.0-alpha
1.0.0-alpha.1
1.0.0-0.3.7
1.0.0-beta.11
1.0.0-rc.1

Pre-release versions indicate the release is unstable. They have lower precedence than the associated normal version: 1.0.0-alpha < 1.0.0. Dot-separated identifiers are compared left to right. Numeric identifiers are compared numerically; alphanumeric are compared lexically. Numeric identifiers always have lower precedence than alphanumeric: alpha.1 < alpha.beta.

Build metadata (+ prefix)
1.0.0+build.1
1.0.0+20240115
1.0.0-beta.1+sha.0e3ab4f
1.0.0+exp.sha.5114f85

Build metadata is for identifying specific builds — CI run numbers, commit hashes, timestamps. Critically, build metadata is IGNORED when determining version precedence. 1.0.0+001 and 1.0.0+20240115 are considered equal. Build metadata may appear after either the patch version or after a pre-release identifier.

Pre-release precedence ordering (from lowest to highest)
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

How package managers interpret semver ranges

Package managers extend semver with range syntax that allows specifying acceptable version ranges rather than a single version. Understanding these operators is essential for reading and writing package manifests.

OperatorExampleEquivalent rangenpm / Cargo / pip
^^1.2.3>=1.2.3 <2.0.0npm default — safe minor/patch updates
~~1.2.3>=1.2.3 <1.3.0npm — patch updates only
>=>=1.2.3>=1.2.3Any version at or above
>>1.2.3>1.2.3Strictly above (not including)
==1.2.31.2.3 exactlyExact pin (also written as 1.2.3 in npm)
x / *1.x>=1.0.0 <2.0.0Wildcard: any minor/patch
range1.2.3 - 2.3.4>=1.2.3 <=2.3.4Inclusive range
||^1.2 || ^2.0^1.2 OR ^2.0Union of ranges (npm)

Rust's Cargo treats 1.2.3 (without a prefix) the same as npm's ^1.2.3 — allowing compatible updates. Python's pip uses == for exact pins and ~= for compatible releases. Go modules use semver with a /vN suffix for major versions above 1 (e.g., github.com/pkg/v2).

Semver in practice — when to bump major vs minor vs patch

Fixed a null pointer exception with no API change
Bug fix with no observable behavior change for code that was working correctly.
PATCH
Added a new optional filter parameter to an existing function
Backward-compatible addition — existing callers still work without passing the new parameter.
MINOR
Removed a deprecated function that was marked for removal
Removal of a public API is a breaking change regardless of how long it was deprecated.
MAJOR
Changed the default value of an optional parameter
Code that relies on the default value is broken. Changing defaults is a breaking change.
MAJOR
Fixed a security vulnerability that changes output format
Security fixes are exceptional. If the format change is necessary for security, document it as MINOR with a security advisory, or MAJOR if feasible. Community practice varies.
MAJOR or MINOR
Added a new module that doesn't affect existing modules
Adding new functionality that existing code never encounters cannot break anything.
MINOR
Narrowed the accepted input type (string → number only)
Rejecting previously accepted inputs is a breaking change even if the type was technically wrong before.
MAJOR

Semver anti-patterns to avoid

Breaking changes in minor releases

The most damaging anti-pattern. Users who run npm update expecting safe updates suddenly have broken code. Always make breaking changes in major releases, even if they seem small.

Fear-driven major versions

Some teams avoid major version bumps to hide the existence of breaking changes, hoping no one notices. Proper semver requires honest signaling — if it breaks, it's a major.

Skipping versions (1.0.0 → 3.0.0)

Version numbers must be incremented sequentially. Skipping versions creates confusion in changelogs and for package managers. Release 2.0.0 even if it's going to be short-lived.

Never reaching 1.0.0

Staying on 0.x.x indefinitely is a red flag — it signals the API is never stable. When your library has a stable, committed public API, release 1.0.0 and honor the semver contract.

Including non-code changes in version bumps

Documentation changes, README updates, and CI config changes don't affect the package's behavior and don't need a version bump. Only functional code changes to the library's behavior warrant a new release.

Releasing without a changelog

Semver signals the type of change, but users need to know what actually changed. A machine-readable changelog (CHANGELOG.md following keepachangelog.com format) is essential for maintainable dependencies.

Breaking change detection in practice

Manually reviewing every change for breaking behavior is error-prone. Several tools exist to automate breaking change detection:

Conventional Commits

A commit message convention that encodes the change type: feat: for features (→ MINOR), fix: for bug fixes (→ PATCH), and feat!: or BREAKING CHANGE: footer for breaking changes (→ MAJOR). Tools like semantic-release parse these commits and automatically determine the correct semver bump and generate changelogs.

TypeScript API Extractor

Microsoft's API Extractor tracks the public API surface of TypeScript packages. It generates an .api.md file that can be committed and diff'd in CI to detect API surface changes, making breaking changes visible in code review.

go-apidiff / apidiff

Go's official tool for detecting API incompatibilities between versions of a Go module. It compares the exported identifiers of two versions and reports any removals or incompatible changes.

next-semver (npm)

Analyzes your Git history using Conventional Commits and suggests the next semver version. Integrates with CI pipelines to automate the release process entirely.

FAQ

Common questions

What is semantic versioning?

Semantic versioning (semver) is a versioning scheme that uses a three-part version number: MAJOR.MINOR.PATCH. MAJOR increments for breaking changes, MINOR for new backward-compatible features, and PATCH for backward-compatible bug fixes. It was created by Tom Preston-Werner (co-founder of GitHub) and is specified at semver.org.

When should I increment the major version?

Increment the major version when you make incompatible API changes. Examples: removing a public function, changing a function's signature, renaming a public class, changing the behavior of an existing feature in a way that breaks existing code. Major version 0 (0.y.z) is for initial development — the public API is not yet stable and anything may change.

When should I increment the minor version?

Increment the minor version when you add new functionality in a backward-compatible manner. Examples: adding new public functions or methods, adding new optional parameters, adding new endpoints to an API. Existing code that uses the library continues to work without modification.

When should I increment the patch version?

Increment the patch version when you make backward-compatible bug fixes that do not change the public API. Examples: fixing a calculation error, fixing a crash, improving performance without changing behavior. If you release a patch, reset the patch number and bump minor: 1.4.2 → 1.5.0, not 1.4.3.

What are pre-release versions?

Pre-release versions have a hyphen suffix: 1.0.0-alpha, 1.0.0-beta.1, 1.0.0-rc.2. Pre-release versions indicate instability and have lower precedence than the release version: 1.0.0-alpha < 1.0.0-beta < 1.0.0. Numeric pre-release identifiers are compared numerically (alpha.1 < alpha.2 < alpha.10), while alphanumeric identifiers are compared lexically.

What is build metadata in semver?

Build metadata is appended with a plus sign: 1.0.0+build.20240115. Build metadata is ignored when determining version precedence — 1.0.0+001 and 1.0.0+20240115 are considered equal. It is used to identify specific builds (CI run IDs, commit SHAs, build timestamps) without affecting version ordering.

What do npm semver range operators mean?

npm supports these range operators: ^ (caret) allows minor and patch updates within the same major (^1.2.3 = >=1.2.3 <2.0.0). ~ (tilde) allows only patch updates (˜1.2.3 = >=1.2.3 <1.3.0). >= allows any version at or above the specified. * or x is a wildcard matching any version. 1.x.x = any minor/patch of major 1.

Does 0.x.x mean the same as 1.x.x?

No. Major version zero (0.y.z) is a special case in semver: the public API is considered unstable and anything may change at any time. A 0.y.z → 0.y+1.z change may contain breaking changes even though only the minor version was bumped. Production dependencies should typically use versions >= 1.0.0 where the stability guarantee applies.

Is my version string valid semver?

A valid semver version matches: MAJOR.MINOR.PATCH where each part is a non-negative integer with no leading zeros, optionally followed by a pre-release suffix (-alpha.1) and/or build metadata (+001). Examples: 1.0.0 ✓, 1.0.0-alpha ✓, 1.0.0-alpha.1+build ✓, 1.0 ✗ (missing patch), 1.0.0.0 ✗ (too many parts), 01.0.0 ✗ (leading zero).

More in Code & Dev