Skip to tool
ecech.
💻 Developer & Code

Why 1.0.10 Sorts Before 1.0.9 Everywhere Except Where It Matters

String sorting puts 1.0.10 before 1.0.9 and 1.0.0-alpha after 1.0.0. Both are wrong. Compare versions properly and see what a caret range really allows.

Sort a list

Sorted as text


  

Sorted as semver


  

What this range allows

Advertisement

How the calculation works

One character, and a very different amount of trust ^1.2.3 1.2.3 up to, but not including, 2.0.0 accepts new features you have never seen ~1.2.3 < 1.3.0 accepts bug fixes only Both rely on the author following the rules. Nothing enforces that a minor release is safe.

How to Use This Tool

Compare two versions, sort a list, or check what a range permits. The text-sorted column is there so you can see what a naive comparison would have told you.

Why string sorting fails

Comparing versions as text compares one character at a time, so 1.0.10 comes before 1.0.9 — the character "1" is less than "9" and the comparison stops there. Anywhere versions are sorted as strings, the tenth patch release will appear older than the ninth.

Correct comparison reads major, minor and patch as numbers and compares them in order. Only if all three are equal does anything else matter.

Pre-release versions sort before the release

This is the part that surprises people, and it is deliberate: 1.0.0-alpha is older than 1.0.0. A pre-release is a version on the way to the release, so it must sort before it.

Within pre-releases, identifiers are compared piece by piece: numeric parts numerically, text parts alphabetically, and a numeric identifier always sorts lower than a text one. That gives the ordering people expect — alpha, then beta, then rc — only because those words happen to be in alphabetical order. 1.0.0-preview would sort after 1.0.0-beta and before 1.0.0-rc, which is alphabetical and not chronological.

What each number promises 1 . 2 . 3 major minor patch majorbreaks something — you must read the notes minoradds something, nothing removed patchfixes something, no new surface All three are promises by the author. Nothing in the tooling enforces them.
The number tells you what kind of reading the release deserves, not whether it is safe.

Caret and tilde

  • ^1.2.3 — anything from 1.2.3 up to but not including 2.0.0. Minor and patch updates are accepted automatically.
  • ~1.2.3 — anything from 1.2.3 up to but not including 1.3.0. Patch updates only.
  • 1.2.3 — exactly that version, nothing else.

There is an exception worth knowing: for versions below 1.0.0 the caret is much stricter, because pre-1.0 releases are not expected to be stable. ^0.2.3 allows only up to 0.3.0, behaving like a tilde. That is why a dependency at 0.x can break on what looks like a minor bump.

What ranges cannot protect you from

Semantic versioning is a promise made by the author, not a property the tooling verifies. Nothing stops a minor release from containing a breaking change, whether by accident or because the author disagreed about what counts as breaking.

So a caret range means "I trust this maintainer to classify their own changes correctly", which is a reasonable default and not a guarantee. A lockfile is what actually makes builds reproducible; the range only decides what a fresh install or an update is allowed to pick.

Advertisement

Frequently Asked Questions

Why does 1.0.10 sort before 1.0.9?
Because it was sorted as text, one character at a time, and "1" is less than "9". Correct comparison reads major, minor and patch as numbers, which puts 1.0.10 after 1.0.9. Any list of versions sorted as strings gets this wrong from the tenth release onward.
Is 1.0.0-alpha newer or older than 1.0.0?
Older. A pre-release is a version on the way to the release, so it sorts before it — 1.0.0-alpha, then 1.0.0-beta, then 1.0.0. This is the opposite of what a string comparison gives and of what most people assume.
What is the difference between ^1.2.3 and ~1.2.3?
The caret allows minor and patch updates, so anything below 2.0.0. The tilde allows patch updates only, so anything below 1.3.0. One character decides whether new features can arrive in your build without you asking for them.
Why does ^0.2.3 behave differently?
Because versions below 1.0.0 are not expected to be stable, so the caret becomes much stricter there and allows only up to 0.3.0 — effectively acting like a tilde. This is why a 0.x dependency can break on what looks like a routine minor bump.
How do pre-release identifiers sort among themselves?
Piece by piece: numeric parts numerically, text parts alphabetically, and a numeric identifier always sorts lower than a text one. Alpha, beta and rc happen to be in alphabetical order, which is why that sequence works — a name like 'preview' would land between beta and rc regardless of when it was released.
Does a caret range guarantee nothing will break?
No. Semantic versioning is a promise by the author, not something the tooling verifies, so a minor release can contain a breaking change by accident or by disagreement about what counts as breaking. A lockfile is what makes builds reproducible; the range only decides what a fresh install may pick.

Related tools in Developer & Code

Browse all Developer & Code tools
The Mac mini the ecech. site is built on, beside a handwritten note reading ecech.com.

Made by one person

ecech. is not a content farm. Every tool here is written and checked by hand, one at a time, by someone who wanted the tool to exist and could not find a version that showed its working.

No accounts and no sign-in, and nothing you type reaches a server — every calculation on this page runs inside your browser. The ads are served by Google and do set their own cookies, which is set out in full on the privacy page. More about the site.