DEV Community

Gajus Kuizinas
Gajus Kuizinas

Posted on • Updated on

When not to use package-lock.json

I maintain over 200 repositories on GitHub and one of the most common PRs that I receive is someone adding package-lock.json or yarn.lock. These PRs are closed without merging because dependency lock files are not designed to be used by packages that are themselves dependencies of other packages.

What's going wrong?

Official NPM documentation encourages to commit package-lock.json files to the source code version control:

It is highly recommended you commit the generated package lock to source control: this will allow anyone else on your team, your deployments, your CI/continuous integration, and anyone else who runs npm install in your package source to get the exact same dependency tree that you were developing on. Additionally, the diffs from these changes are human-readable and will inform you of any changes npm has made to your node_modules, so you can notice if any transitive dependencies were updated, hoisted, etc.

– https://docs.npmjs.com/files/package-locks#using-locked-packages

Committing package-lock.json to the source code version control means that the project maintainers and CI systems will use a specific version of dependencies that may or may not match those defined in package.json. Because package-lock.json cannot be added to NPM registry (by design; see NPM shrinkwrap), projects that depend on a project that uses package-lock.json will themselves use package.json to resolve project's dependencies, i.e. what works for project maintainers/ CI systems might not work when the project is used as a dependency.

The origin of this misuse is NPM documentation. It should instead explain that package-lock.json should only be committed to the source code version control when the project is not a dependency of other projects, i.e. package-lock.json should only by committed to source code version control for top-level projects (programs consumed by the end user, not other programs).

I have already asked NPM to update the documentation, but it was archived without an action.

Responding to criticism

Some comments suggested that the biggest advantage of package-lock.json is that it allows to replicate the development environment.

I would support a variation of package-lock.json if it could somehow only apply to devDependencies. I can see some (albeit small and with tradeoffs) benefit to wanting your development environment not break if there is a broken release among your dependencies. I would personally prefer my environment to break and become aware that a dependency in my toolkit requires attention (and depending on the nature of the issue either offer help, subscribe to an issue or replace the dependency). After all, you can easily patch your dependency tree if you need to lock down a specific version for development purposes.

However, there is no such option and using lock files at the moment will create the risks described in this article – namely that the dependencies that you use do not match those that your users will depend on. Responsible development requires that your script works with the latest versions of dependencies satisfied by semver (and yes that includes transitive dependencies).

Oldest comments (48)

Collapse
 
stereobooster profile image
stereobooster

I get it - similar idea expressed in this article yehudakatz.com/2010/12/16/clarifyi.... But from experience npm packages are fragile, what you were able to install today (based on package.json) doesn't guarantee you would be able to install in a month. How do you deal with fragility? I gave up and commit lock files.

Collapse
 
robogeek profile image
David Herron

You can always use exact version number dependencies in package.json. Package-lock.json is unnecessary.

Collapse
 
drkn profile image
Maciej Dragan

Your "exact version number dependencies" have other dependencies which most likely are not "exact version number dependencies", so case described by @stereobooster still applies. You will most likely get different packages in time when you use npm install on your project without package-lock file, and your project may break because of that. I agree it's a pain to maintain it but sometimes there is no other way.

Collapse
 
brieucp profile image
brieucp

Instead it's better to set npm with --save-exact (in .npmrc or on every install).

Collapse
 
carlosnufe_97 profile image
Carlos Núñez

The headline is misleading, there are as you already told different use cases.

Collapse
 
andrewsosa profile image
Andrew Sosa

c l i c k b a i t

Collapse
 
gajus profile image
Gajus Kuizinas • Edited

There is only so much information that can be contained in the title. Do you have suggestions how to rephrase the title?

Collapse
 
rdjoseph profile image
RDJ

"When not to use package-lock.json"

Thread Thread
 
gajus profile image
Gajus Kuizinas

Thank you for the suggestion. I have updated article title.

Collapse
 
melroysblog profile image
Melroy's Blog

Keep pushing, I think this is an important improvement.

Collapse
 
jascomp profile image
Jason Holden

Thanks for the clarification.

Collapse
 
arcanis profile image
Maël Nison • Edited

Not again :(

This is incorrect, the lockfiles should always be committed. This isn't about you, but about your external contributors and project archival. The lockfile ensures that we never lose track of the last known good state.

Previous discussion: twitter.com/arcanis/status/1164229...

Collapse
 
gajus profile image
Gajus Kuizinas

You have not read the article.

Collapse
 
arcanis profile image
Maël Nison • Edited

I did. And working on package managers is part of my daily job, so it's not the first time I think about this.

My thread is a good explanation of the problems in your reasoning, and I'd be happy to go into more details if you have a specific point you'd like to challenge.

Thread Thread
 
gajus profile image
Gajus Kuizinas

I am not advocating against using package-lock.json for programs designed to be consumed by the end consumer. I do suggest not to use lock files in packages that will be dependencies of other packages.

I have read comments of the multiple people who are suggesting to use package-lock.json because it locks down dev dependencies. This is not a valid use case for the same reasons that I outline in the article. If you need to lock down dev dependencies, use semver range that meets your requirements.

Thread Thread
 
arcanis profile image
Maël Nison • Edited

If you need to lock down dev dependencies, use semver range that meets your requirements

Exact semver range isn't locking. Transitive dependencies are still free to change unless you use a proper lockfile.

This isn't to say that exact dependencies are useless (some of my codebases use them), but they solve a different problem and it shouldn't impact at all whether the lockfiles are meant to be committed or not.

I am not advocating against using package-lock.json for programs designed to be consumed by the end consumer

This is the part I'm objecting to. To quote myself:

A lockfile isn't meant to prevent breakages [for consumers] - it's to ensure smooth deployments and development cycles. The first isn't super relevant to libraries and tooling, but the second very much is.

Thread Thread
 
gajus profile image
Gajus Kuizinas • Edited

I understand your argument (using lock files ensures smooth deployments and development cycles). However, unlike others in this thread, I do not put as much value on this argument compared to the downsides I have described in this article. In practise, I have found it extremely rare that dependencies or transitive dependencies break or introduce bugs within semver changes that prevent me from working or that would have been prevented using lock files. Happened, maybe 3 times over the last 5 years that my work was interrupted for longer than an hour.

Thread Thread
 
matthiasccri profile image
Matthias • Edited

It could have been 0 times, if you used a lockfile. Just saying.

Collapse
 
robogeek profile image
David Herron

The state of dependencies should be described in package.json. That's what the dependencies field is for. Package-lock.json is unnecessary.

Collapse
 
evolutionxbox profile image
Jonathan Cousins • Edited

Not that this is common, but what about the dependencies that your dependencies rely on? What if they change?

Collapse
 
bycedric profile image
Cedric van Putten • Edited

I agree that your lockfile must not be packaged and shipped within the library.

However, when developing libraries you probably have a set of development dependencies and/or normal dependencies. Here is where I disagree, because these should actually be in a lockfile (in my opinion). You are still pulling dependencies there, even ones not included in the publishes library.

The alternative of using exact versions is also possible. Although, for me, the tradeoffs of messy commit to update patches and losing the ability of quick updates (remove lockfile/npm update) is a no-go for me.

Collapse
 
saurabhdaware profile image
Saurabh Daware 🌻

I did not understand how not commiting package-lock solves this issue? won't package.json will update sub-dependencies anyway?

Collapse
 
bennypowers profile image
Benny Powers 🇮🇱🇨🇦

On the other hand, wouldn't you like to lock versions of devDependencies, leverage faster builds in ci, etc?

Collapse
 
coderbyheart profile image
Markus Tacker 🇳🇴

Because package-lock.json cannot be added to NPM registry

This is false.

Collapse
 
adiddy profile image
A

If the package-lock.json has a dependency outside a range defined in package.json, the lock file will be updated with the exact version used. Therefore, they always match.

I tried not using a package-lock.json. After the second instance of a dependency's dependency breaking my build, it became obvious that the lock file is there for a reason... I always use it now so everyone gets the exact same versions.

Collapse
 
nullvoxpopuli profile image
NullVoxPopuli

Not everyone adheres to semver. That's where this falls apart.

Most popular package that disregards semver: typescript

Collapse
 
krohrsb profile image
Kyle Brown

The solution for the stated problem is not not using lock files. It should be choosing pinned dependencies vs ranges.

Collapse
 
ljharb profile image
Jordan Harband

No, it's not, because pinning dependencies doesn't pin their dependencies. The only way to do this properly in an application is with a lockfile (NOT pinning anything). The only way to do this properly in a package is to use ^, always.

Collapse
 
ankurloriya profile image
Ankur Loriya

I think package-lock.json for security purpose.
When a user hit npm install package-lock.json created commit the package-lock.json changes to version control. They must be insecure network.

Once the package-lock.json generated from true (secure) network and your other machine network might under attack and hacker might change npm registry DNS/Route/IP in that case npm will check the integrity with npm install.

Collapse
 
fulopattila122 profile image
Attila Fulop

I'm maintaining PHP packages and we have the same debate about composer.lock in libraries.

I'm lucky to live in the same city as one of the two authors of composer (npm's counterpart in php world) so once I could have a personal discussion about the topic with him.

He also suggested to commit the lock file with the package (which I don't do either) but he also suggested to do this in the CI pipeline:

  • test the package with the deps as in the lockfile
  • downgrade direct deps to the lowest version allowed by .json file and test against that
  • upgrade the direct deps to the highest version allowed by .json file and test against that as well

Here I can see the benefit of the lockfile, albeit I haven't started doing it yet

Collapse
 
kgrosvenor profile image
kgrosvenor • Edited

Dependency lock files are for fast tracking your dependencies via a file cache, so it doesn't have to look through npm again to find them again, you are meant to commit it yes and i don''t think there is a case for not commiting it?

It also works the same on composer.

Why ignore his advise about that from the author? your PI pipeline versions will eventually mess up because you don't commit it...