Lockfile Management
Lock file strategies for deterministic installs across npm, pnpm, and Yarn
You are an expert in lock file management for JavaScript/TypeScript projects, including strategies for deterministic installs, resolving conflicts, and maintaining lock file hygiene across npm, pnpm, and Yarn.
## Key Points
- Every resolved package version
- The resolved tarball URL or integrity hash
- The dependency tree structure (which packages depend on which)
- Integrity checksums (SHA-512 hashes) for tarball verification
1. A committed lock file
2. Using `--frozen-lockfile` (pnpm), `--immutable` (Yarn), or `npm ci` in CI
- **v1**: npm 5-6. Flat dependency list.
- **v2**: npm 7-8. Includes both v1 and v2 data for backward compatibility.
- **v3**: npm 9+. Drops v1 data, smaller file size.
- **npm workspaces**: Single `package-lock.json` at root.
- **pnpm workspaces**: Single `pnpm-lock.yaml` at root (configurable with `shared-workspace-lockfile`).
- **Yarn Berry workspaces**: Single `yarn.lock` at root.
## Quick Example
```yaml
# Yarn Berry (.yarnrc.yml)
resolutions:
lodash: 4.17.21
```
```json
{
"resolutions": {
"lodash": "4.17.21"
}
}
```skilldb get package-management-skills/Lockfile ManagementFull skill: 282 linesLock File Management — Package Management
You are an expert in lock file management for JavaScript/TypeScript projects, including strategies for deterministic installs, resolving conflicts, and maintaining lock file hygiene across npm, pnpm, and Yarn.
Core Philosophy
Overview
Lock files record the exact resolved version of every dependency in a project's dependency tree. They ensure that every developer, CI server, and production deployment installs the same dependency versions, regardless of when install is run. Without a lock file, version ranges in package.json could resolve to different versions at different times.
Core Concepts
Lock File by Package Manager
| Manager | Lock file | Format |
|---|---|---|
| npm | package-lock.json | JSON |
| pnpm | pnpm-lock.yaml | YAML |
| Yarn Classic | yarn.lock | Custom |
| Yarn Berry | yarn.lock | YAML (v2+ format) |
What a Lock File Contains
- Every resolved package version
- The resolved tarball URL or integrity hash
- The dependency tree structure (which packages depend on which)
- Integrity checksums (SHA-512 hashes) for tarball verification
Deterministic Installs
A deterministic install means: given the same package.json + lock file, every install produces the exact same node_modules tree (or PnP map). This requires:
- A committed lock file
- Using
--frozen-lockfile(pnpm),--immutable(Yarn), ornpm ciin CI
lockfileVersion
npm's package-lock.json has evolved:
- v1: npm 5-6. Flat dependency list.
- v2: npm 7-8. Includes both v1 and v2 data for backward compatibility.
- v3: npm 9+. Drops v1 data, smaller file size.
Implementation Patterns
Installing with Lock File Integrity
# npm: clean install from lock file (deletes node_modules first)
npm ci
# pnpm: fail if lockfile is out of date
pnpm install --frozen-lockfile
# Yarn Berry: fail if lockfile would change
yarn install --immutable
Use these commands in CI to guarantee deterministic builds. Never use npm install in CI — it may modify the lock file.
Updating Dependencies
# npm: update within range and rewrite lock file
npm update
# npm: update a specific package
npm update lodash
# pnpm: update interactively
pnpm update --interactive
# pnpm: update all to latest (ignoring ranges)
pnpm update --latest
# Yarn Berry: update within ranges
yarn up
# Yarn Berry: update to latest
yarn up '*'
Resolving Lock File Merge Conflicts
Lock file conflicts are common in active repositories. The correct resolution:
# npm: accept either side, then regenerate
git checkout --theirs package-lock.json
npm install
# pnpm: accept either side, then regenerate
git checkout --theirs pnpm-lock.yaml
pnpm install
# Yarn Berry: has built-in merge resolution
# First, accept both sides, then:
yarn install
# Yarn auto-resolves the lock file conflicts
Never manually edit lock files to resolve conflicts. Always regenerate through the package manager.
Deduplication
Over time, lock files accumulate duplicate versions of the same package:
# npm: deduplicate resolved versions
npm dedupe
# pnpm: deduplicate
pnpm dedupe
# Yarn Berry: deduplicate
yarn dedupe
# Check for duplicates without fixing
yarn dedupe --check
Auditing Lock File Contents
# List all resolved versions
npm ls
# Find why a specific package is installed
npm explain lodash
pnpm why lodash
yarn why lodash
# Check for duplicate versions
npm ls --all | grep "lodash@"
Overriding Resolved Versions
Sometimes you need to force a specific resolution:
// npm (package.json)
{
"overrides": {
"lodash": "4.17.21",
"foo>bar": "2.0.0"
}
}
// pnpm (package.json)
{
"pnpm": {
"overrides": {
"lodash": "4.17.21"
}
}
}
# Yarn Berry (.yarnrc.yml)
resolutions:
lodash: 4.17.21
Or in package.json for Yarn:
{
"resolutions": {
"lodash": "4.17.21"
}
}
Enforcing a Single Package Manager
Use corepack and the packageManager field to ensure everyone uses the same tool:
{
"packageManager": "pnpm@9.15.0",
"engines": {
"node": ">=20"
}
}
corepack enable
# Now running `npm install` in a pnpm project will fail
Add an .npmrc or preinstall script as a second guard:
{
"scripts": {
"preinstall": "npx only-allow pnpm"
}
}
Lock File in Monorepos
- npm workspaces: Single
package-lock.jsonat root. - pnpm workspaces: Single
pnpm-lock.yamlat root (configurable withshared-workspace-lockfile). - Yarn Berry workspaces: Single
yarn.lockat root.
All three managers use a single lock file for the entire monorepo, which ensures cross-workspace dependency consistency.
Reviewing Lock File Changes in PRs
Large lock file diffs are hard to review. Strategies:
# See a human-readable summary of what changed
npm diff
# For pnpm, compare resolved versions
pnpm ls --depth=0 > before.txt
# (after update)
pnpm ls --depth=0 > after.txt
diff before.txt after.txt
On GitHub, lock file diffs are collapsed by default. Use lockfile-lint or socket.dev for automated review.
Lock File Linting
npx lockfile-lint \
--path package-lock.json \
--type npm \
--allowed-hosts npm \
--validate-https
This checks that all resolved URLs use HTTPS and point to the official registry, catching supply chain tampering.
Best Practices
- Always commit the lock file to source control. Never add it to
.gitignore. - Use
npm ci/--frozen-lockfile/--immutablein CI. Never run bareinstall. - Never manually edit lock files. Always regenerate through the package manager.
- Run
npm dedupe/pnpm dedupe/yarn dedupeperiodically to reduce duplication. - Use
packageManagerinpackage.jsonwith corepack to enforce the same package manager and version. - Review lock file changes in PRs. Unexpected new packages or registry URL changes may indicate supply chain attacks.
- Use lockfile-lint to validate lock file integrity in CI.
- When resolving merge conflicts, accept one side and regenerate — never hand-merge.
- For applications, consider using exact versions in
package.jsonplus the lock file for maximum reproducibility. - For libraries, use
^ranges inpackage.jsonand do not publish the lock file (it is ignored by consumers anyway).
Common Pitfalls
- Not committing the lock file: Results in non-deterministic installs. Every team member may get different versions.
- Using
npm installin CI instead ofnpm ci:npm installmay update the lock file silently, meaning CI installs differ from what was tested locally. - Lock file from wrong package manager: Having both
package-lock.jsonandpnpm-lock.yamlin the same project confuses tools and developers. Use only one. - Stale lock file: Running
npm installwithout the lock file present (or after deleting it) resolves the latest versions within ranges, which may differ from what the team has tested. - Force-resolving conflicts incorrectly: Accepting "ours" or "theirs" on a lock file conflict without regenerating can leave the lock file inconsistent with
package.json. - Publishing lock files in libraries: npm ignores lock files in dependencies. Publishing
package-lock.jsonin a library adds size but provides no value. - Ignoring lockfileVersion upgrades: Upgrading npm may change
lockfileVersion, causing churn. Coordinate npm version upgrades across the team. - Phantom dependencies after lock file regen: Regenerating a lock file may resolve newer transitive versions that introduce bugs or incompatibilities. Always run tests after regeneration.
Anti-Patterns
Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.
Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.
Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.
Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.
Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.
Install this skill directly: skilldb add package-management-skills
Related Skills
Bundling Libraries
Bundling JavaScript/TypeScript libraries for distribution using tsup, unbuild, and Rollup
Dependency Audit
Security auditing npm dependencies for vulnerabilities, license compliance, and supply chain risks
Npm Publishing
Publishing packages to the npm registry with proper configuration, access control, and release automation
Pnpm
pnpm workspace management for monorepos with content-addressable storage and strict dependency isolation
Private Registries
Setting up and using private npm registries with Verdaccio, GitHub Packages, and GitLab Package Registry
Semantic Versioning
Semantic versioning (SemVer) conventions, version ranges, and strategies for managing breaking changes