The Lockfile (package-lock.json)

Discover how lockfiles guarantee deterministic builds by freezing exact versions, integrity hashes, and resolution URLs.

Package Managers3 min readConcept 9 of 24

The Lockfile Guarantee

To solve the problem of floating SemVer ranges, modern package managers automatically generate a **lockfile** (package-lock.json for npm, yarn.lock for Yarn, pnpm-lock.yaml for pnpm).

The lockfile is an exact, frozen snapshot of your entire dependency tree. It records the exact version of every package installed, down to the deepest sub-dependency.

When you run npm install in a project that has a package-lock.json, npm ignores the ranges in package.json and installs the exact versions listed in the lockfile. This guarantees a **deterministic build**: every developer and server gets the exact same code, every time.

Anatomy of a locked dependency

Notice two critical fields inside a lockfile:

1. **resolved**: The exact URL where the tarball was downloaded. This skips the step of asking the registry 'where is this package?'.

2. **integrity**: A cryptographic hash of the file contents. Before npm extracts the downloaded package, it verifies the hash. If the package was tampered with in transit (or the registry was compromised), the hash won't match, and npm aborts the installation.

Handling Merge Conflicts

Because lockfiles are large, auto-generated JSON files, they frequently cause Git merge conflicts when multiple developers add dependencies on different branches.

Instead of resolving lockfile conflicts manually, resolve the conflicts in your package.json first, then run npm install. npm will automatically regenerate the package-lock.json based on the merged package.json, cleanly resolving the conflict for you.

Lockfile Generator Simulator

Click to resolve dependencies; observe how lockfiles freeze exact versions with integrity hashes and resolved tarball URLs. Shows merge conflict resolution strategy.

package.json
{
  "name": "my-app",
  "dependencies": {
    "react": "^18.2.0"
  }
}
^18.2.0 means "any compatible version". Without a lockfile, different installs might get 18.2.0, 18.2.1, or 18.3.0!
package-lock.json
No lockfile generated yet.
Click "npm install" to resolve the tree and freeze versions.
{
  "name": "my-app",
  "lockfileVersion": 3,
  "packages": {
    "node_modules/react": {
      "version": "18.2.0",      "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",      "integrity": "sha512-V12pzJcy...==",      "engines": {
        "node": ">=0.10.0"
      }
    }
  }
}
Exact version locked for reproducible builds.
Tarball URL cached to skip registry lookups.
Checksum guarantees file wasn't tampered with.
<<<<<<< HEAD
    "node_modules/react": {
      "version": "18.2.0",
      "resolved": "..."
=======
    "node_modules/react": {
      "version": "18.3.1",
      "resolved": "..."
>>>>>>> feature-branch

Merge Conflict!

Never resolve package-lock.json manually. Fix the conflict in package.json, then run npm install to regenerate the lockfile cleanly.

Remember this

  • Lockfiles guarantee deterministic builds by freezing exact versions across teams.
  • They store resolved URLs for speed and integrity hashes for security.
  • Never manually resolve a lockfile merge conflict; fix package.json and run npm install.

Done with this concept?

Mark it complete to track your progress. No login needed.