Course:Node.js & Express/
Lesson

Every npm package has a version number, and those numbers are not random, they follow a contract called semantic versioningWhat is semantic versioning?A numbering system (major.minor.patch) that communicates whether a release contains breaking changes, new features, or bug fixes.. Understanding this contract tells you, at a glance, whether upgrading a dependencyWhat is dependency?A piece of code written by someone else that your project needs to work. Think of it as a building block you import instead of writing yourself. is likely to break your code or just quietly improve it.

The three-part version number

Think of a version like 2.4.1 as three independent dials:

2 . 4 . 1
│   │   │
│   │   └── PATCH - bug fixes, no new features
│   └────── MINOR - new features, backwards compatible
└────────── MAJOR - breaking changes, update your code

The promiseWhat is promise?An object that represents a value you don't have yet but will get in the future, letting your code keep running while it waits. of semver is that package authors commitWhat is commit?A permanent snapshot of your staged changes saved in Git's history, identified by a unique hash and accompanied by a message describing what changed. to this meaning. A PATCH release means "we fixed something, you can safely upgrade." A MINOR release means "we added something, your existing code keeps working." A MAJOR release means "something changed fundamentally, read the migrationWhat is migration?A versioned script that changes your database structure (add a column, create a table) so every developer and server stays in sync. guide."

In practice, authors sometimes get this wrong. But semver gives you a baseline expectation that makes dependencyWhat is dependency?A piece of code written by someone else that your project needs to work. Think of it as a building block you import instead of writing yourself. management tractable.

When each number increments

TypeWhen to bumpExample
PATCHBug fix, security patch, performance improvement4.1.04.1.1
MINORNew function or option added, old ones still work4.1.14.2.0
MAJORFunction renamed, parameter order changed, feature removed4.2.05.0.0

When MAJOR bumps, MINOR and PATCH both reset to zero. When MINOR bumps, PATCH resets.

02

Version range operators

The version string in your package.json is not necessarily an exact version, it is a range that npm uses when resolving which version to install or update to.

Exact version

json
{ "express": "4.18.0" }

Installs exactly 4.18.0. Never auto-updates. Useful for security-critical packages where you need certainty, but you lose automatic bug fixes.

Caret (^), the default

json
{ "express": "^4.18.0" }

Accepts anything >= 4.18.0 and < 5.0.0. This is what npm install writes by default and is the recommended choice for most dependencies. You get bug fixes and new features without the risk of breaking changes.

Tilde (~), conservative

json
{ "express": "~4.18.0" }

Accepts anything >= 4.18.0 and < 4.19.0. Only PATCH updates allowed. Use this when you want to be extra careful that a dependencyWhat is dependency?A piece of code written by someone else that your project needs to work. Think of it as a building block you import instead of writing yourself.'s behaviour does not change, even in backwards-compatible ways.

The wildcard * means "any version." Never use it in production, you are opting in to every future breaking change.
03

package-lock.jsonWhat is json?A text format for exchanging data between systems. It uses key-value pairs and arrays, and every programming language can read and write it.: the real source of truth

Your package.json has ranges; package-lock.json has exact versions. When npm installs packages, it records the precise version it chose along with a checksum:

json
{
  "name": "express",
  "version": "4.18.2",
  "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
  "integrity": "sha512-..."
}

This means two developers running npm install on the same project get byte-for-byte identical node_modules, even if a new patch release came out between their installs. Always commitWhat is commit?A permanent snapshot of your staged changes saved in Git's history, identified by a unique hash and accompanied by a message describing what changed. package-lock.json to source control.

04

Checking and updating packages

# See which installed packages have newer versions available
npm outdated

# Update all packages within their allowed semver ranges
npm update

# Install a specific package at its latest version (ignores your range)
npm install express@latest

# Preview what an install would do without changing anything
npm install express@latest --dry-run

A useful third-party tool for managing updates is npm-check-updates:

npx npm-check-updates      # Show what could be updated
npx npm-check-updates -u   # Write new ranges to package.json
npm install                # Actually install them
Always check the changelog before running ncu -u. It bumps ranges mechanically, it will happily upgrade a MAJOR version without warning you about breaking changes.
05

Pre-release versions

Packages in active development often publish pre-release versions before a stable release:

1.0.0-alpha.1    - very early, expect breakage
1.0.0-beta.2     - feature-complete but not stable
1.0.0-rc.1       - release candidate, final testing

npm does not install pre-release versions unless you ask explicitly:

npm install some-package@beta
npm install some-package@1.0.0-rc.1
06

Choosing the right strategy

ScenarioRecommended range
Most production dependencies^4.18.0 (caret)
Security-sensitive packages4.18.2 (exact)
Conservative production~4.18.0 (tilde)
Dev tools (jest, eslint)^8.0.0 (caret is fine)
WildcardNever in production