Versions and dependencies

Use lockfiles with purpose

Treat the lockfile as the reproducible resolution for development and CI while publishing compatible ranges for consumers.

package.json declares allowed versions. package-lock.json records the exact graph npm resolved, including integrity hashes. They solve different problems.

For a library like @acme/slugify-title, consumers read your semver ranges and resolve their own tree. They do not use your lockfile. You still commit one so CI and collaborators install the same versions you tested.

What the lockfile is for

When I clone the repo and run:

npm ci

npm installs exactly what the lockfile specifies. If package.json and the lockfile disagree, npm ci fails instead of guessing.

That failure is good. It means someone changed a range without regenerating the lock.

What the lockfile is not for

Libraries should not publish package-lock.json to npm. npm ignores it in published packages. Do not delete the lockfile every time an update misbehaves. That hides which package moved.

Instead, update in small steps:

npm update unicode-properties
git diff package-lock.json
npm test

Review the diff. You are looking for unexpected new packages, changed integrity lines, or lifecycle scripts you did not expect.

CI should use clean installs

In GitHub Actions I run npm ci, not npm install, for deterministic builds. The same command should pass locally before you merge.

Ranges vs pinned resolution

Keep caret ranges in package.json for runtime deps unless you have a documented reason to pin tighter. The lockfile carries the pin for your workspace. Consumers stay free to resolve within your declared compatibility window.

If npm ci passes on Monday and fails on Tuesday with no code changes, the lockfile diff from a dependency update is the first place I look.

Lesson completed