Versions and dependencies

Read version ranges

Predict which releases satisfy caret, tilde, exact, comparison, tag, and prerelease specifications.

A dependency range in package.json is an update policy written as a string. It tells npm which releases may be installed. Copying ^ prefixes without understanding them is how you get surprise breakages in CI.

The ranges you will see most

{
  "dependencies": {
    "unicode-properties": "1.0.1",
    "fast-check": "~3.23.0",
    "tsup": "^8.0.0"
  }
}
  • 1.0.1 (exact): only that version. Safe for fragile pins, but you will miss patches.
  • ~3.23.0 (tilde): allows patch updates within 3.23.x, not 3.24.0.
  • ^8.0.0 (caret): allows compatible updates per semver rules. For 8.0.0, npm admits >=8.0.0 <9.0.0.

Test a range before you commit it

npm ships a semver checker:

npx semver -r '^8.0.0' 8.0.1 8.4.0 9.0.0

Output:

8.0.1
8.4.0

Version 9.0.0 is omitted because it falls outside the caret range for a 8.x dependency.

For zero-major packages the rules shift. Test them explicitly:

npx semver -r '^0.2.0' 0.2.1 0.3.0 1.0.0

Here 0.3.0 is excluded. Caret ranges on 0.x are tighter than on 1.x.

Prereleases and tags

A version like 2.0.0-beta.1 will not satisfy ^2.0.0 unless you opt into prereleases. Tags such as latest and next point at different dist-tags on the registry. Most apps should follow latest unless they chose otherwise.

Pick ranges on purpose

For @acme/slugify-title, I pin runtime dependencies narrowly when a patch release already burned me once. For build tools in devDependencies, a caret on the major version is usually fine because consumers never install them.

When you change a range, note why in the PR. “Copied from another repo” is not a policy.

The npm outdated command shows what newer versions exist without writing the lockfile yet. I scan that output weekly, then update one package at a time with evidence instead of clicking “update all”.

Lesson completed