Package vs Package-lock JSON File in Node.js

Makesh Kumar
Bits and Pieces
Published in
7 min readMar 12, 2023

--

You might have definitely come across this package-lock.json file if you are working on projects where Node.js is used.

In this article, we will see what is package-lock.json file, why it is required, and how it is different from the package.json file.

What is a package.json file?

It is a JSON file that is present in the root directory of the project and it holds important information about the project, it basically contains the metadata about the project and all the dependencies that were installed using NPM.

Whenever we install any package using NPM, the package.json file will be updated with that package name and the version installed in semver notation.

What is semver?

NPM follows the standard called semver (semantic versioning) for versioning its package.
According to semver, all versions will have 3 digits in the format (x.y.z)
where,
x — is the major version
y — is the minor version
z — is the patch version

When a new version of the package is going to be released means, based on the changes/ implications, we have to update the version accordingly,

  • Increase the major version, if any big changes were introduced/new features were implemented.
  • Increase the minor version, if any new functionality is added in a backward-compatible manner.
  • Increase the patch version, if any bugs were fixed.

Versioning symbols

Along with the semantic version, NPM adds special symbols before these versions in the package.json file, these symbols are used to tell npm which version to be installed when we do npm update or npm install.

Each symbol has its own rule and it defines how to resolve to version while installing any packages. some of the commonly used symbols are caret ( ^ ) and tilde ( ~ ).

caret (^)

Rule: Minor and patch versions can be changed, but not the major version. For example, if we have the version as ^13.1.0in the package.json file, it means when we do an npm update,

  • it can be updated to versions like 13.2.0, 13.3.0, 13.3.1, 13.3.2, etc., ( all these versions have minor and patch changes )
  • but not to 14.0.0 or anything above this, ( here major version is changed, as per the ^ rule, it is not accepted )

tilde (~)

Rule: Only the patch version can be updated.
For example, if we have a version as ~13.1.0 in the package.json file, it means when we do an npm update,

  • it can be updated to versions like 13.1.1, 13.1.3 ( all these versions have only patch changes ),
  • but not to versions like 13.2.0, 13.3.0( here minor version is changed, as per the ~ rule, it is not accepted ),
  • but not to 14.0.0or anything above this, ( here major version is changed, as per the ~ rule, it is not accepted ).

NPM by default adds the ^ rule to all packages

Now we understood how NPM maintains the package versions, but there is a problem with this approach.

Let’s understand the problem by taking one real-time scenario:

Consider a project where Developer A installed a package called “abc” of version “2.1.1”. In the package.json file, it might look like this:

"abc": "^2.1.1"

Now, after 3 months, Developer B joins the team.

In the meantime, package “abc” had new-release versions like — 2.1.3, 2.1.5, 2.2.1, 2.2.6, 3.0.0, 3.0.1, and so on.

So when Developer B clones the project and does the npm installmeans NPM would try to install the latest version as per the rule mentioned.

In our case, the latest version of package “abc” is “3.0.1”, but as per the ^ rule, “ 3.0.1” is not compatible (since it has a major version change). Now NPM tries to find the latest available package with a version that satisfies our ^ rule and it finds version “2.2.6” is the latest package, which satisfies our condition (only patch and minor version changes).

So it installs package “abc” of version “2.2.6” in Developer B’s machine.

Now comes the problem:

Developer A is using a package of version 2.1.1 and Developer B is using a 2.2.6 version of the same package.

Now the things that work for Developer A may not work for Developer B even though they are using the same package.json file.

This is where package-lock.json files come into action to help, as the name says, it is the locked version of package.json.

What is a package-lock.json file?

It is the locked version of the package.json file, it will be generated automatically when we install/modify any package using NPM. The main purpose of this file is to keep track of the exact version of the packages that were installed. It will also contain information about the sub-dependencies (packages that were installed automatically as dependencies for the packages that we installed)

How package-lock.json file works?

Whenever we do an npm install, NPM will scan the package.json file to find the package name and it scans the package-lock.json file to find the version of that package.

When it finds the version mentioned in the lock file is compatible with the version mentioned in the package.json file (based on semver notation), it will install the package of the version mentioned in the lock file.

Let’s understand this via the same scenario that we discussed above.

When Developer A tries to install a package called “abc” of version “2.1.1”, it installs that package and updates the package.json file with the package name and the version installed along with semver notation (usually ^).
Also, it updates the pacakge.lock.json file with the package name and the exact version (2.1.1) which was installed.

Now, after 3 months, Developer B joins the team. In the meantime, package “abc ” had new release versions like — 2.1.3, 2.1.5, 2.2.1, 2.2.6, 3.0.0, 3.0.1, and so on.

Now when Developer B clones the project and does the npm installmeans NPM scans the package.json file and finds the package called “abc”, and then it will search for that package in the package-lock.json file to find its version.

  • When it finds the package “abc” in the package-lock file,
    it will check whether the version mentioned in the lock file is compatible with the package.json file, in our case version 2.1.1 is compatible with “^2.1.1”
  • It will install the package “abc” of the version mentioned in the lock file (which is the exact version “2.1.1” that Developer A installed 3 months ago)
  • Now Developer A and Developer B will have the same version package installed on their machine.
  • When the package “abc” was not there in the package-lock file or the package-lock file itself wasn’t there, NPM installs the package as per the version and semantic notation mentioned in the pacakge.json file, which in our case package “abc” of version “2.2.6” would be installed on Developer B’s machine.
    This is exactly the same problem we discussed in the previous example.

To conclude,

— Having a package-lock.json file ensures that whenever we do an npm install, the exact same version will be installed all the time.

— It’s recommended to commit the package-lock.json file along with our project source code so that all the developers and CI/CD will use this file to install the same version of the package.

— It’s not recommended to change anything in the package-lock.json file explicitly, all the updates should happen only by updating the package.json file.

💡 Pro Tip: If you’re using Node.js for your entire back end, you could use tools like Bit here to simplify the entire process. It would let your developers abstract themselves from the entire workflow and toolset required and they’d be able to compose and deploy different versions only using a single tool. Learn more here and here.

I hope this article helps you in some way to understand the difference between package.json and package-lock.json file.

You can read more about it from npm docs.

Thanks for reading! 😉
Follow me on Linkedin and Medium for more such content.

Keep learning and keep coding!

Build Apps with reusable components, just like Lego

Bit’s open-source tool help 250,000+ devs to build apps with components.

Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.

Learn more

Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:

Micro-Frontends

Design System

Code-Sharing and reuse

Monorepo

--

--