Sitemap
Dirishala

Dirishala

Understanding Package Lock — NPM

4 min readFeb 3, 2026

Understanding `package-lock.json`

What is `package-lock.json`?

- `package-lock.json` is an automatically generated file created by **npm** (Node Package Manager) when you run commands like `npm install`.
- It locks the versions of the dependencies (and their dependencies) installed in a project.
- Its primary purpose is to ensure **deterministic installs**, meaning every time you or someone else installs the project dependencies, the exact same dependency tree is installed.

Why is `package-lock.json` important?

1. **Consistency Across Environments:**
— Ensures that all developers, CI/CD pipelines, and production environments use the exact same versions of dependencies.
— Avoids the “works on my machine” problem caused by differences in dependency versions.

2. **Faster Installs:**
— npm can quickly resolve dependencies using the lock file without recalculating dependency versions.

3. **Security and Auditing:**
— Helps in tracking and auditing the exact versions of packages used.
— Vulnerability scanners can analyze the lock file to detect security issues.

4. **Version Control:**
— Should be committed to version control systems (like Git) to maintain a record of exact dependency versions.

How does `package-lock.json` work?

- When you run `npm install`, npm reads the `package.json` file and installs the dependencies.
- It then generates or updates the `package-lock.json` file with:
— Exact versions of all installed packages.
— The resolved URLs from where the packages were fetched.
— Integrity hashes for package verification.
— The full dependency tree, including nested dependencies.

Key Fields in `package-lock.json`

- **name**: The name of the project.
- **version**: The version of the project.
- **lockfileVersion**: Version of the lockfile format.
- **requires**: Indicates if the package requires dependencies.
- **dependencies**: An object listing all dependencies with:
— `version`: The exact version installed.
— `resolved`: The URL where the package was downloaded.
— `integrity`: A hash to verify package integrity.
— `requires`: Sub-dependencies of the package.

Example snippet:

```json
{
“name”: “example-project”,
“version”: “1.0.0”,
“lockfileVersion”: 2,
“dependencies”: {
“lodash”: {
“version”: “4.17.21”,
“resolved”: “https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
“integrity”: “sha512-v2kDE…”,
“requires”: {}
}
}
}
```

Differences Between `package.json` and `package-lock.json`

| Feature | package.json | package-lock.json |
| Purpose | Declares project dependencies | Locks exact dependency versions |
| Edited by | Developers | Generated and updated by npm |
| Version specificity | Can specify version ranges (e.g., `¹.0.0`) | Specifies exact versions installed |
| Should be committed? | Yes | Yes |
| Human-readable? | Yes | Mostly machine-readable |

Get Dirishala’s stories in your inbox

Join Medium for free to get updates from this writer.

Best Practices

- Always commit `package-lock.json` to your repository.
- Do not manually edit `package-lock.json`.
- Use `npm ci` for clean installs in CI environments, which uses `package-lock.json` to install exact versions.
- Regularly update dependencies and regenerate the lock file to keep dependencies secure and up to date.
- Use `npm audit` to check for vulnerabilities in dependencies based on the lock file.

Common Commands Related to `package-lock.json`

- `npm install`: Installs dependencies and updates `package-lock.json`.
- `npm ci`: Installs dependencies exactly as specified in `package-lock.json` (faster and more reliable in CI).
- `npm audit`: Checks for security vulnerabilities in dependencies.
- `npm update`: Updates packages and regenerates `package-lock.json` accordingly.

Understanding Versions: `package.json` vs `package-lock.json`

One of the key concepts to grasp when working with npm dependencies is the difference between how versions are specified in `package.json` and how they are recorded in `package-lock.json`.

Version Specifications in `package.json`

- The `package.json` file lists your project’s dependencies and the **version ranges** you want to allow.
- These version ranges use **semver (semantic versioning)** notation to specify compatible versions, such as:
— Exact version: `”lodash”: “4.17.21”` — only version 4.17.21 will be installed.
— Caret (`^`): `”lodash”: “⁴.17.0”` — compatible with 4.x.x versions, i.e., >=4.17.0 <5.0.0.
— Tilde (`~`): `”lodash”: “~4.17.0”` — compatible with patch versions, i.e., >=4.17.0 <4.18.0.
— Greater than or equal: `”lodash”: “>=4.17.0”` — any version from 4.17.0 upwards.
- This flexibility allows your project to automatically receive minor or patch updates when you run `npm install`, which can include bug fixes or new features.

Exact Versions in `package-lock.json`

- The `package-lock.json` file records the **exact versions** of every package that was installed at the time of the last `npm install`.
- This includes not only your direct dependencies but also all nested (transitive) dependencies.
- For example, even if your `package.json` says `”lodash”: “⁴.17.0”`, the `package-lock.json` will specify `”lodash”: “4.17.21”` (or whichever exact version was installed).
- This exact version locking ensures that anyone installing the project later will get the **same dependency tree** with the same package versions, regardless of new releases that might satisfy the version range in `package.json`.

Why This Difference Matters

- **Flexibility vs Stability:**
— `package.json` allows flexibility in dependency versions to benefit from updates.
— `package-lock.json` ensures stability by locking down the exact versions to avoid unexpected changes.
- **Reproducible Builds:**
— Without `package-lock.json`, running `npm install` at different times may install different versions if new versions matching the range are published.
— With `package-lock.json`, the exact same versions are installed every time, making builds reproducible.
- **Updating Dependencies:**
— To update dependencies to newer versions allowed by `package.json` ranges, you run commands like `npm update` or manually change version ranges.
— This updates `package-lock.json` with the new exact versions.

### Example

Suppose your `package.json` contains:

```json
“dependencies”: {
“express”: “⁴.17.0”
}
```

- This means npm can install any `express` version `>=4.17.0` and `<5.0.0`.
- When you run `npm install`, npm might install `express@4.17.3` (latest patch at that time).
- The `package-lock.json` will record:

```json
“express”: {
“version”: “4.17.3”,
“resolved”: “…”,
“integrity”: “…”
}
```

- Later, if a new version `4.17.4` is released, running `npm install` **without** a `package-lock.json` might install `4.17.4`.
- But with the lock file present, npm installs `4.17.3` exactly, ensuring consistency.

--

--