Skip to main content

Understanding package.json And package-lock.json

Β· 5 min read
Sivabharathy

Learn the key differences between package.json and package-lock.json, how they impact Node.js projects, and best practices for using them in GitHub Actions. Ensure consistent, reliable builds with real-world examples and actionable tips.

In every Node.js project, package.json and package-lock.json are essential files for managing dependencies. Though they may seem similar, they serve different purposes and have a significant impact on the consistency, security, and stability of your application β€” especially when deploying or testing via GitHub Actions.

In this post, you'll learn:

  • The difference between package.json and package-lock.json
  • Real-world examples of how these files affect your project
  • Best practices for using them
  • How to use them effectively in GitHub Actions CI/CD pipelines

πŸ“˜ What is package.json?​

The package.json file is the heart of any Node.js project. It serves as a manifest that describes:

  • The project name, version, and metadata
  • The list of dependencies and dev dependencies
  • Scripts for building, testing, and running your project
  • Configuration for tools like Babel, ESLint, Jest, etc.

βœ… Example package.json​

{
"name": "my-node-app",
"version": "1.0.0",
"scripts": {
"start": "node index.js",
"test": "jest"
},
"dependencies": {
"express": "^4.18.0"
},
"devDependencies": {
"jest": "^29.0.0"
}
}

This file tells npm what your project needs to function.


πŸ”’ What is package-lock.json?​

The package-lock.json file is automatically generated by npm when you install dependencies. It locks the exact versions of all dependencies and their sub-dependencies, creating a snapshot of the entire dependency tree.

Why it's important:​

  • Ensures consistent installs across environments
  • Avoids unexpected bugs from updated sub-dependencies
  • Makes CI/CD and production environments predictable
  • Speeds up installations using npm ci

🧠 Real-World Difference​

Let’s say your package.json has:

"dependencies": {
"axios": "^1.3.0"
}

This allows any version from 1.3.0 up to but not including 2.0.0.

Now:

  • On Monday, you run npm install β†’ you get axios@1.3.1
  • On Friday, your teammate runs npm install β†’ they get axios@1.3.3

That can lead to bugs that only happen on some machines.

However, if your project has a committed package-lock.json, then everyone gets axios@1.3.1, including GitHub Actions and production servers.


βœ… Best Practices​

PracticeRecommendation
Commit package-lock.jsonβœ… Always for apps, optional for libs
Use npm ci in CI/CDβœ… Guarantees reproducibility
Don’t manually edit lock file❌ Let npm manage it
Handle merge conflicts carefully⚠️ Lock files can conflict
Audit your dependenciesβœ… Use npm audit or GitHub Dependabot

πŸ’₯ What Happens Without package-lock.json?​

ProblemCause
"Works on my machine" bugsDifferent versions of sub-dependencies
CI builds randomly failTransitive dependencies change
Debugging becomes harderCan't recreate the environment

πŸ” Using package-lock.json in GitHub Actions​

In CI/CD, stability and speed are everything. You should use:

npm ci

instead of

npm install

Why npm ci?​

  • Faster install times
  • Fails if package-lock.json and package.json are out of sync
  • Exactly reproduces dependency tree from lock file

πŸ”§ Example GitHub Actions Workflow​

name: Node.js CI

on: [push, pull_request]

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: "18"

- name: Install dependencies
run: npm ci

- name: Run tests
run: npm test

This guarantees your tests and builds are always run against the exact same versions of dependencies.


🌍 Real-World Scenario​

🚨 Scenario: Bug in Production​

  • On Monday, you deploy to staging β†’ axios@1.3.1
  • On Friday, you deploy to production β†’ axios@1.3.3
  • A new breaking change in axios@1.3.3 causes errors in production, but not in staging.

Had you committed and used the package-lock.json, both environments would use the exact same version, preventing the bug from reaching production.


πŸ“¦ Should You Commit package-lock.json?​

Project TypeCommit Lock File?Why?
Applicationsβœ… YesLocks everything for stable builds
Libraries⚠️ OptionalConsumers manage their own deps

πŸ§ͺ Bonus: Useful Commands​

# Clean install using lock file
npm ci

# Update only the lock file based on current package.json
npm install --package-lock-only

# Audit security vulnerabilities
npm audit

πŸš€ Conclusion​

package-lock.json is more than a side effect of npm install β€” it's a powerful tool to ensure consistent and reliable environments.

When used correctly, it:

  • Prevents bugs caused by changing dependencies
  • Speeds up and stabilizes your CI/CD pipelines
  • Helps deliver a consistent experience from dev to production

Tip: Always commit your package-lock.json and use npm ci in automation workflows like GitHub Actions.


πŸ’¬ Have thoughts or questions?​

Drop them in the comments β€” or reach out if you'd like help setting up rock-solid GitHub Actions workflows for your Node.js projects.