All posts
4 min read

Semantic Versioning Explained: What 2.4.1 Actually Means

Version numbers like 2.4.1 follow a convention: major.minor.patch. What each number promises, what ^ and ~ mean in package.json, why '0.x' versions are different, how lock files fit in, and how to version your own app or library.

getting startedtoolingdeveloper experiencebeginner

Open package.json and you'll see dependencies like "react": "^19.1.0" and "zod": "~3.24.2". Those numbers — and the little ^ and ~ in front — follow a convention called semantic versioning. Understanding it tells you which updates are safe and which might break your app.

Three numbers, three promises

A semantic version has three parts: MAJOR.MINOR.PATCH.

  2  .  4  .  1
  │     │     └── PATCH: bug fixes, nothing else
  │     └──────── MINOR: new features, nothing broken
  └────────────── MAJOR: breaking changes
  • PATCH (2.4.1 → 2.4.2) — bug fixes. Your code should work unchanged.
  • MINOR (2.4.1 → 2.5.0) — new features added in a backwards-compatible way. Your code should still work.
  • MAJOR (2.4.1 → 3.0.0) — something changed in a way that may break your code: a function removed, renamed, or behaving differently.

When a number goes up, the ones to its right reset to zero: 2.4.1 → 2.5.0 → 3.0.0.

The whole system is a promise from the library's authors. Most keep it well. Some don't — which is why tests and lock files still matter.

Versions before 1.0

Versions starting with 0 — like 0.8.3 — mean "still in early development." The usual promises are relaxed: in practice, a change in the minor number (0.8 → 0.9) may break things. Treat 0.x updates with care.

Pre-release tags

You'll also see versions like 3.0.0-beta.2 or 5.0.0-rc.1. These are pre-releases: test versions before the real release. Package managers don't install them unless you ask specifically.

^ and ~ in package.json

The symbol in front of a version tells your package manager which versions are acceptable when installing:

Written Allows Meaning
"19.1.0" exactly 19.1.0 Pinned
"~19.1.0" 19.1.x Patch updates only
"^19.1.0" 19.x.x Minor and patch updates (npm's default)
"*" or "latest" anything Risky — avoid

(For 0.x versions, ^ is more cautious: ^0.8.3 allows only 0.8.x.)

So "^19.1.0" says "any version 19 that's at least 19.1.0 is fine." That's why two people running npm install a month apart could, in principle, get different versions. (npm and package.json Explained.)

Lock files fix that

Your lock file — package-lock.json, pnpm-lock.yaml, or yarn.lock — records the exact version of every package that was installed, including dependencies of dependencies. Commit it. With it, everyone (including your server) installs the identical set.

  • npm install respects the lock file, updating it only if package.json changed.
  • npm ci installs exactly what the lock file says, and fails if it doesn't match — the right choice for servers and CI. (GitHub Actions CI Basics.)

Updating dependencies safely

Semantic versioning tells you how careful to be:

  • Patch and minor updates: usually safe. Update, run your tests, check the app.
  • Major updates: read the changelog or migration guide first. Update one major dependency at a time, on a branch, and test properly.
npm outdated        # what's behind, and by how much
npm update          # apply updates allowed by your ^ and ~ ranges

How to Update Dependencies Safely covers the full process.

A note on AI tools: models were trained on older versions of libraries and sometimes write code for a previous major version — calling functions that were renamed or removed. If an AI-written call fails with "is not a function," check which major version you have installed against which one the code assumes.

Versioning your own project

For an app that only you deploy, you don't strictly need version numbers — Git commits and deploy dates identify versions. But they help if you:

  • Publish a library or API others depend on — then follow semantic versioning carefully. Breaking your users' code in a minor release loses their trust.
  • Ship a mobile or desktop app — app stores require version numbers.
  • Want readable release notes — "what changed in 1.4?"

A changelog (CHANGELOG.md) listing what changed in each version is a kind thing to keep. (What Is Markdown?.) Some teams generate version numbers and changelogs automatically from commit messages written in a structured style. (How to Write Good Commit Messages.)

The summary

  • MAJOR.MINOR.PATCH — breaking, features, fixes.
  • 0.x — anything may change.
  • ^ allows minor updates; ~ allows patches only.
  • Commit your lock file; use npm ci on servers.
  • Read the changelog before a major update.

EasySpawn gives each project a persistent workspace where Claude Code can update dependencies on a branch, run your tests, and show you what changed before anything reaches production. See how it works or join the waitlist.

Related: How to Update Dependencies Safely · npm and package.json Explained · npm Supply Chain Security

Keep reading