noted.do
The Tech Guy

What Those Three Numbers in a Version Mean

Version 2.14.3. Most people read that as "a newer one than 2.14.2" and move on, which is fine. But if you write code that depends on other people's code, or you maintain something other people depend on, those numbers are a promise. At least they're supposed to be.

The convention most projects follow is called semantic versioning, or semver. The format is MAJOR.MINOR.PATCH, and each part tells you something specific about what changed.

PATCH goes up for bug fixes that don't change how anything is used. 2.14.2 to 2.14.3 should be safe to install without thinking about it.

MINOR goes up when new features are added in a way that doesn't break existing use. Your code that worked on 2.14 should still work on 2.15. You just get extra stuff you can choose to use. When minor goes up, patch resets to zero.

MAJOR goes up when something breaks compatibility. A function was removed, a setting was renamed, the default behavior changed. Going from 2.x to 3.0 means "read the upgrade notes before you do this." Minor and patch both reset.

That's the whole idea, and it's really useful when people stick to it. It lets you upgrade with some confidence, and it lets package managers make sensible decisions automatically.

How package managers use it

This is where it gets practical. In a project's dependency file you usually don't pin an exact version. You give a range. In npm's package.json, for example:

"dependencies": {
  "some-library": "^2.14.3",
  "other-library": "~1.4.0"
}

The caret ^2.14.3 means "anything from 2.14.3 up to, but not including, 3.0.0." You'll get new minor and patch releases, but never a new major. The tilde ~1.4.0 is stricter: "1.4.0 up to, but not including, 1.5.0," so patch updates only. Composer for PHP, Cargo for Rust and others use similar symbols, though the exact rules differ a little between tools, so check the docs for yours.

One quirk worth knowing: versions starting with 0 are treated as unstable. Semver says anything can change at any time before 1.0.0, and npm's caret reflects that by being stricter there (^0.4.2 only allows up to 0.5.0, not 1.0.0).

Pre-releases

You'll also see things like 3.0.0-beta.2 or 3.0.0-rc.1. The bit after the dash marks a pre-release. It sorts before the final 3.0.0, and package managers generally won't pick it up unless you ask for it explicitly.

The honest caveat

Semver is a social agreement, not a guarantee. A maintainer can ship a patch release that accidentally breaks something, or decide that a change wasn't "really" breaking when it was for you. Plenty of popular projects also don't use semver at all. Some use calendar versioning like 2026.10, some bump the major every year on a schedule, and browsers and many apps just increment one big number.

That's why lock files exist. package-lock.json, composer.lock, Cargo.lock and friends record the exact version that was actually installed, so everyone on the project and your production server get identical code. Commit your lock file. Then update dependencies on purpose, run your tests, and commit the new lock file when you're happy.

If you're publishing something yourself, my advice is to follow semver and take it seriously, especially the major number. Bumping it feels dramatic, but it's much kinder to your users than a "minor" update that breaks their build.

Comments

Be kind. See the rules.