Why does package-lock.json exist, and how does it work? (2024)

When writing JavaScript applications, you describe its dependencies using a package.json file. This file contains all of your applications direct dependencies and their versions.

If you’re building a RESTful API using Express, for, example, your package.json file will probably list express as a dependency.

{ "name": "my-app", "version": "1.0.0", "main": "index.js", "dependencies": { "express": "4.0.0" }}

Whenever you ask others to clone and run your application, they need to install its dependencies using npm install. Doing so will cause them to download the version 4.0.0 of Express.

Why does package-lock.json exist, and how does it work? (1)

Your application's direct dependency on Express.

When installing Express, a package upon which your application depends, you will also download all of the packages upon which Express itself depends. Among the packages upon which Express depends, you can find, for example, send, a package which provides a static file server.

Why does package-lock.json exist, and how does it work? (2)

Express depends on `send`, which causes it to be downloaded when installing your application's dependencies.

Additionally, when installing Express and, consequently, send, you will have to install the dependencies of send. Because send depends on mime-types, , a utility package to handle mime-types, NPM will install also install mime-types.

Why does package-lock.json exist, and how does it work? (3)

The `send` package depends upon `mime-types`.

Finally, when installing mime-types, NPM will notice that mime-types depends on mime-db, and will install mime-db too.

Why does package-lock.json exist, and how does it work? (4)

NPM downloads the `mime-db` package when installing your application's dependencies because it's a dependency of other dependencies.

The process of recursively downloads all of your packages’ dependencies and the dependencies of those dependencies is what causes your node_modules folder to get heavy so quickly.

Even though you only listed express as a dependency, if you check your node_modules folder — the folder in which NPM stores dependencies — you will see that there are many more packages there.

▾ node_modules/ ▸ accepts/ ▸ array-flatten/ ▸ body-parser/ ▸ bytes/ ▸ content-disposition/ ▸ content-type/ ▸ cookie-signature/ ▸ cookie/ ▸ debug/ ▸ depd/ ▸ destroy/ ▸ ee-first/ ▸ encodeurl/ ▸ escape-html/ ▸ etag/ ▸ express/ ▸ finalhandler/ ▸ forwarded/ ▸ fresh/ ▸ http-errors/ ▸ iconv-lite/ ▸ inherits/ ▸ ipaddr.js/ ▸ media-typer/ ▸ merge-descriptors/ ▸ methods/ ▸ mime-db/ ▸ mime-types/ ▸ mime/ ▸ ms/ ▸ negotiator/ ▸ on-finished/ ▸ parseurl/ ▸ path-to-regexp/ ▸ proxy-addr/ ▸ qs/ ▸ range-parser/ ▸ raw-body/ ▸ safe-buffer/ ▸ safer-buffer/ ▸ send/ ▸ serve-static/ ▸ setprototypeof/ ▸ statuses/ ▸ toidentifier/ ▸ type-is/ ▸ unpipe/ ▸ utils-merge/ ▸ vary/ package-lock.json package.json

These packages are not only Express’ dependencies, but also the dependencies of all dependencies.

This dependency download process would be beautiful if it weren’t for the fact that npm install doesn’t necessarily install the same dependencies given the same package.json file.

Running npm install in the afternoon can cause you to get different dependencies when compared to the npm install your colleague ran in the morning, even though none of you changed the version of Express upon which the application depends, for example.

Having different dependencies is a problem because it can cause the application to present inconsistent behaviour across different environments, which is yet another cause for headaches when you’re trying to build reliable software.

When listing dependencies in your package.json file, you can prefix their versions with a ^ or ~. Each of these symbols will tell NPM that it doesn’t necessarily need to download the exact version you specified. Instead, it will try to download a version which is “compatible” or “equivalent” to the one in package.json.

Imagine, for example, that you prepended ^ to Express’ version number in your package.json.

{ "name": "my-app", "version": "1.0.0", "main": "index.js", "dependencies": { "express": "^4.0.0" }}

In that case, if Express’ maintainers release a fix and bump their version number to 4.0.1, running npm install would install the 4.0.1 version, not the 4.0.0 as you’ve written in package.json.

Similarly, when Express’ maintainers release a new feature and bump their version number to 4.1.0, the npm install command will download 4.1.0, which should be compatible with the 4.0.0 version you listed in your package.json.

Most of the time, using ^ or ~ should not cause problems, because, according to semantic versioning, bumping minor — the middle version number — or patch — the rightmost version number — means that there are no breaking changes.

Why does package-lock.json exist, and how does it work? (5)

Bumping the leftmost version number indicates there have been breaking changes. The middle version number indicates a release contains new features. The rightmost number indicates fixes.

However, given enough time, all humans make mistakes. They may, for example, bump the minor or patch when there’s a breaking change, causing builds to break like I once did when I incorrectly released breaking changes to Chai and bumped the wrong version number, causing millions of builds to break around the world.

So does this mean that not using ^ or ~ causes npm install to always download the same dependency tree? No, it doesn’t 🙃

Even though you may not add ^ or ~ in front of your dependencies’ versions, your dependencies may list their dependencies using ^ or ~, which can cause npm install to download different versions depending on the time you’ve run it.

If you’ve specified Express’ 4.0.0 version as a dependency, but Express itself listed send as a dependency using ^1.0.0, for example, you could end-up downloading the version 1.0.1 of send in the morning, and version 1.0.2 in the afternoon.

Therefore, if the send package’s maintainers release breaking changes and incorrectly tag them as 1.0.2, your application could break even though you locked your application’s dependencies by avoiding ^ and ~.

The package-lock.json file lists your application’s dependencies and the dependencies of all its dependencies. In other words, it describes which version of every single package you have installed. That’s why it’s so much longer than package.json.Whenever you use package-lock.json to install dependencies, you will get the exact same packages, it doesn’t matter whether you’re installing them now or in a thousand years when we’ll hopefully not be using keyboards anymore.

To install dependencies based on your package-lock.json file, you should use npm ci instead of npm install.

By using npm ci you will be able to avoid problems which happen when an application works in your machine, but doesn’t work in someone else’s because they ended up downloading different dependencies due to using npm install.

That’s why, whenever running your builds in a continuous integration server, you should install dependencies using npm ci instead of npm install. Doing so will cause the CI server’s build to run just like it had run in your machine.

The reason package-lock.json may change automatically when you run npm install is because NPM is updating the package-lock.json file to accurately reflect all the dependencies it has downloaded since it may have gotten more up-to-date versions of some of them. Once NPM updates the package-lock.json file, others can get those exact same versions by using npm ci if they want.

Sometimes, evil human-beings will obtain control of other people’s NPM credentials and release new versions of packages by bumping the package’s patch version number, causing millions of dependants to download the malicious piece of code.

It’s enough for a single package in your entire dependency tree to be compromised for you to end-up downloading the malicious code. Sometimes, the compromised package will not even be a direct dependency of your application or even a dependency of one of your application’s dependencies. It may be, for example, a dependency of a dependency of a dependency of a dependency.

Because npm ci will cause you to download the exact same packages every time, it will be less likely for you to install malicious code given you have taken the necessary security precautions.

Most of the time, package maintainers will bump the correct version numbers when releasing features or fixes, and there is no harm in having these features and fixes in. If anything, they may make your application faster or solve a bug you have.

In other words, having npm install makes it easier to get the “best” versions of packages: the ones with more features and fixes.

Essentially, npm install and npm ci exist for slightly different purposes. While the former installs the latest possible versions of your dependencies, the latter installs the exact same versions you had when you last used npm install.

An additional advantage of using npm ci is its quicker execution. The npm ci command runs faster than npm install because it doesn’t need to check what’s the latest compatible version of a package. Instead, it knows exactly which version to fetch thanks to the exhaustive dependency list in package-lock.json. In some cases, dependency installations can happen twice as fast.

The fact that install doesn’t by default use package-lock.json, is somewhat controversial, though. Some people would advocate that updating dependencies by default is significantly dangerous and that one should be very explicit when doing it.

Personally, I like to use npm install whenever I’m installing dependencies myself, and npm ci in all my scripts.

I use npm install when installing dependencies myself because, in that case, I can vet which versions I get and make sure that I’ll be using the latest versions of packages, which can include bug fixes, performance improvements, and new features.

On the other hand, I don’t want any of my scripts to update dependencies because machines running automated builds can’t vet the packages they get. Instead, I want these scripts to reproduce the dependencies I had during development time.

Additionally, I want machines’ builds to be deterministic. Given the same commit hash, I want today’s build to yield the same results as next month’s build.

In regards to pinning dependencies, I only pin those dependencies whose minor or patch version bumps cause my application not to work as it should. On other cases, I’m okay to get automatic updates and vet them myself.

A package-lock.json file is the equivalent of a negative film. If you want others to reproduce the exact same photo you’ve taken, you can handle them your negative film, and every print from that negative will look precisely the same.

Similarly, when others install your application’s dependencies using a package-lock.json file, they will get the exact same dependencies, every single time.

The package.json file, on the other hand, is like a list of instructions on describing how you’ve taken a particular photo. It may include, for example, the camera you’ve used, the location from which the photo was taken, and the time of day you did the click.Even though others may follow your instructions to the letter, their photos will be slightly different depending on the weather or the surrounding environment.

In the same way, when others install dependencies using a package.json file, their dependency tree will be similar to yours, but not exactly the same.

Throughout this post, I’ve used ^ and ~ interchangeably; however, they have slightly different semantics.

The ~ character downloads the latest possible version with fixes. If you have specified a version using ~1.0.1, for example, you may get the packages’ 1.0.2 or 1.0.11 versions, but not 1.1.0.

The ^ character, on the other hand, allows you to download any non-breaking versions. In case you specify a version using ^1.1.0, you may get, for example, 1.2.0, or 1.1.2.

You can find the complete documentation for how semantic versioning works in NPM in this link.

Why does package-lock.json exist, and how does it work? (2024)
Top Articles
Tips for Being Successful in the Peer-to-Peer Lending Industry
Expert Home Staging Tips To Make A Small House Feel Bigger
Skyward La Feria
Americanexpress.com/Myplatcard
High Stakes Homework With My Stepmom
Silver Spring, MD to Nanuet, NY Bus
Stone-Ladeau Funeral Home | Winchendon, Massachusetts
Everyhome For Sale
The 5 Best Smart Water Leak Detectors Compared
Cvs Pcr Appointment
Skip The Games Maui
Michelob Ultra Peace Treaty Commercial Cast
Babylon (2022) Stream and Watch Online
Lifeselector F95
WHAT WE CAN DO | Arizona Tile
WelcHOME Lakeside Holiday Homes - Official Website
10 Overhyped Movies That Lived Up To Their Ridiculously High Expectations
Pollen Count In Brandon Fl
184€ Flug Frankfurt am Main - Lahore - Günstige Flüge von Frankfurt am Main nach Lahore - KAYAK
Unc Healthchart
University of Kentucky · Uk Turfland Clinic Pharmacy · 2195 Harrodsburg Rd, Room T1636, Lexington, KY 40504-3504 · Pharmacy
Projectxyz Employee Portal
Devotion Showtimes Near Regency Buenaventura 6
Gavin Jostad Obituary
Nm Ose
New Age Lifestyle Blog
Which Country Has Hosted A Summer Olympics Microsoft Rewards
Synthesis: Faculty AI Literacy Assessment
84 Lumber Price Sheet 2023
UK rap star announces retirement and reveals final show and album after MOBO win
Missed Connections Buffalo Ny
Brazos County Mugshots Busted Newspaper
11 Vintage Houses That Came from a Catalog
Craigslist Ct Apartments For Rent
Outlet For The Thames Crossword
Brent Yorgey - Solved Kattis problems
Egizi Funeral Home Turnersville Nj
Lanna Crabtree
Froedtert Billing Phone Number
Kamado Joe 457 Manual
Barivel Weight
Fttc.instructure
Bad Soden: Stadtplan, Tipps & Infos | ADAC Maps
Mlb Hitting Streak Record Holder Crossword Clue
My Juno Personal Start Page
Texas Longhorns Soccer Schedule
Kitra Somers
Sarah Dreyer Obituary
Urban Ice Organics Coupon Code
Salary Calculator UK - Salary After Tax
Latest Posts
Article information

Author: Amb. Frankie Simonis

Last Updated:

Views: 6148

Rating: 4.6 / 5 (76 voted)

Reviews: 83% of readers found this page helpful

Author information

Name: Amb. Frankie Simonis

Birthday: 1998-02-19

Address: 64841 Delmar Isle, North Wiley, OR 74073

Phone: +17844167847676

Job: Forward IT Agent

Hobby: LARPing, Kitesurfing, Sewing, Digital arts, Sand art, Gardening, Dance

Introduction: My name is Amb. Frankie Simonis, I am a hilarious, enchanting, energetic, cooperative, innocent, cute, joyous person who loves writing and wants to share my knowledge and understanding with you.