DEV Community

Cover image for "But It Works on My Machine! 🤷‍♂️ How package-lock.json Saves the Day"
Ashish Jha
Ashish Jha

Posted on

"But It Works on My Machine! 🤷‍♂️ How package-lock.json Saves the Day"

BASICS

Ever had a project work perfectly on your machine, but when your friend (or worse, your CI/CD pipeline) runs it, everything falls apart like a Jenga tower? Yeah, blame dependencies.

In a Node.js project, package.json is like your shopping list—it says, "I need React, but I’m cool with any version above 18.2.0.(denoted by ^)" Meanwhile, package-lock.json is the receipt—it says, "You got React 18.2.3 from this exact store, and don’t even think about changing it!" It locks the exact versions of not just your dependencies, but also their dependencies’ dependencies (yep, it's dependencies all the way down). This means that when someone else runs npm install, they get the exact same versions as you—no surprise updates, no weird bugs, no "But it works on my machine!" moments.

Now, let’s break down exactly why package-lock.json is the hero we never knew we needed.

package.json

Purpose: Defines the project's dependencies, metadata, scripts, and configurations.

Contains:

  • Project name, version, and description.
  • Scripts (npm start, npm test, etc.).
  • Dependencies (dependencies, devDependencies, peerDependencies, etc.).
  • Version ranges for dependencies, like:
"dependencies": {
  "react": "^18.2.0",
  "axios": "~1.4.0"
}

Enter fullscreen mode Exit fullscreen mode

Versioning:
Uses semantic versioning (SemVer) ranges:

  • "^1.2.3" → Allows updates up to <2.0.0 (i.e., 1.x.x updates).
  • "~1.2.3" → Allows updates up to <1.3.0 (i.e., 1.2.x updates).
  • "1.2.3" → Locks the dependency to exactly 1.2.3.

package-lock.json

Purpose: Ensures deterministic installs by locking exact versions of dependencies and all nested dependencies.

Contains:

  • Exact versions of direct and transitive (nested) dependencies.
  • Resolved URLs from where dependencies were installed.
  • Integrity checksums for security and verification.

Why Do We Need Both?

package.json:

  • Defines what dependencies your project needs.
  • Allows flexibility for minor updates.
  • Useful for open-source projects to let users install compatible versions.

package-lock.json

  • Ensures that everyone working on the project installs the exact same versions.
  • Prevents "works on my machine" problems.
  • Improves performance by caching resolved dependency versions.

What Happens If You Delete package-lock.json?

Running npm install will generate a new package-lock.json file with potentially different versions for dependencies, especially if they were specified with version ranges in package.json.

Should You Commit package-lock.json?

✅ Yes, especially for teams and production apps, to ensure consistency across environments.
🚫 No, if you're building a reusable package/library (e.g., an npm package), because the consumer should decide which versions to use.

NOTE:
If you're using yarn, it creates a yarn.lock file instead of package-lock.json, serving the same purpose.

And that’s the magic of package-lock.json—keeping your project safe from the dreaded “But it works on my machine!” chaos. Hopefully, now you see why this little file is your best friend when it comes to dependency management.

Got questions? Confused about something? Or just want to rant about npm breaking your build at the worst possible time? Drop your thoughts in the comments! I’d love to hear them (and maybe cry with you over dependency nightmares). 😆👇

Top comments (0)