Builder Academy · Dependency Management · Beginner

Lockfiles & reproducible builds

Why does CI stop before lint? Learn the difference between a package manifest and a lockfile, then practice diagnosing a real-world Next.js version mismatch.

What you will learn

1. The shopping list and the receipt

The manifest

package.json

"next": "16.4.0"

The package version that the project requests.

The resolved receipt

package-lock.json

"next": "16.3.5"

The outdated version pinned for installations. It no longer satisfies the manifest.

Think of package.json as the ingredients you order, and package-lock.json as the detailed receipt listing exact versions, where they were resolved, and integrity information. The lockfile also records transitive dependencies: packages your direct dependencies rely on.

A package registry such as npm is the catalog from which packages and metadata are fetched. Integrity hashes help npm check that downloaded package contents match what the lockfile records; they are not a security endorsement.

2. Why npm ci refuses to continue

Continuous Integration (CI) uses npm ci because it checks that the manifest and lockfile are consistent before installing. A mismatch stops dependency installation before ESLint, tests, or the build can run. This is a dependency-resolution failure, not evidence that your source code has lint errors.

Example: package.json requests Next.js 16.4.0, but the lockfile pins 16.3.5.

Result: npm ci exits with an EUSAGE error because the dependency resolution is out of sync.

Fix: regenerate the lockfile, validate it with a clean install, and commit the result.

3. Repair the dependency graph

  1. git switch fix/dependency-security-remediation

    Work on the existing repair branch rather than changing production directly.

  2. npm install --package-lock-only --ignore-scripts

    Resolve and rewrite the lockfile without running package lifecycle scripts or reinstalling node_modules. It still needs registry access.

  3. npm ci

    Reinstall from the committed dependency resolution; npm will reject manifest/lockfile drift.

  4. npm run lint

    Check code quality only after dependencies have installed successfully.

  5. npm run build

    Verify the application compiles. Type checks or stylesheet imports can fail independently of a valid lockfile.

  6. git add package-lock.json && git commit -m "fix(deps): sync lockfile"

    Commit the exact resolved dependency tree for GitHub Actions and teammates.

Finish by pushing the branch and reviewing the fresh GitHub Actions results. If the clean install passes but the build fails, diagnose that new error separately. Never hand-edit integrity hashes, blindly run npm audit fix --force, or disable CI simply to get a green badge.

4. Practice: diagnose before fixing

Scenario: A security PR updates Next.js and eslint-config-next to 16.4.0, but GitHub Actions fails at npm ci. What should you change first?

A. Change npm ci to npm install in CI

Not recommended. This hides drift instead of ensuring CI uses a reproducible dependency resolution.

B. Regenerate and commit the lockfile

Correct. Run npm install --package-lock-only, test with npm ci, then commit the new lockfile.

C. Revert the security patch

Not a default fix. Preserve the intended safe versions unless testing reveals a documented incompatibility.

5. What to explore next

Try changing a dependency version in a disposable practice branch without changing the lockfile, then observe npm ci fail. Restore the manifest or regenerate the lockfile, and verify the command passes. Use a synthetic practice project—not production credentials.

Related lesson: Build Rate Limits & CI/CD

References: npm ci, package-lock.json, npm install.