Use npm ci in CI and deploys, and reserve npm install for deliberate local changes. npm ci installs exactly what the lockfile says and fails if the lockfile and package.json disagree, so an AI assistant that edits one without the other cannot slip a different dependency tree into a build.
What the two commands do differently
The npm documentation describes npm ci as meant for automated environments where you want a clean, reproducible install. In contrast with npm install:
- It needs an existing
package-lock.jsonornpm-shrinkwrap.json. - If the lockfile does not match
package.json, it exits with an error instead of updating the lockfile. - It cannot add individual dependencies.
- It removes any existing
node_modulesfirst. - It never writes to
package.jsonor the lockfiles.
npm install resolves versions, may update the lockfile, and can add packages. That flexibility is what you want while developing and what you do not want in a pipeline.
Why this matters with AI assistants
Assistants frequently edit package.json to add a library, then leave the lockfile stale or regenerate it wholesale. Two bad outcomes follow.
- The build resolves fresh versions at install time, so what you tested is not what you ship.
- A regenerated lockfile changes dozens of transitive packages in a diff nobody reads.
With npm ci, the first problem becomes a loud failure. The second still needs a human to look at the lockfile diff.
A workable setup
- Commit the lockfile.
- Run
npm ciin every pipeline. - Require review for any change to the manifest or lockfile.
- Turn on a dependency review check so added and updated packages, including indirect ones, show up on the pull request.
GitHub's dependency review shows added, removed and updated dependencies with release dates, covers lockfile changes to indirect dependencies, and can fail a pull request on vulnerable packages.
One caveat
The npm docs note that if a lockfile was created with flags that change the tree, such as legacy peer dependency handling, you need to pass the same flags to npm ci. A project level .npmrc keeps them consistent.
Also remember that npm ci still runs package lifecycle scripts after installing. A frozen install makes the tree predictable; it does not make the packages trustworthy.
Common mistakes
Teams sometimes run npm install in CI because it is the habit, which silently rewrites the lockfile during the build. Others delete the lockfile when a merge conflict appears, then let the assistant regenerate it. Both throw away the review you did earlier. Resolve lockfile conflicts by rebasing and regenerating from the reviewed manifest, then read the resulting diff before merging.
Quick check
Does npm ci protect against a malicious package that is already in the lockfile?
No. It guarantees you install what was locked, not that the locked packages are safe. Pair it with review of additions and vulnerability scanning.
Sources
Last reviewed: 19 October 2026.