9

I know the main advantages of package-lock.json and I agree with that. It not only locks the downloaded version in the last install, but also the uri... and that's required on most cases for being possible to replicate the most similar project as possible.

But one thing that seems weird to me is that package.json has the feature of declaring a dependency like dependency: ^1.0.0, that should make npm to download the most recent and compatible version of that package in each installation.

I'm working at a project that I actually need this. Otherwise every time my dependency releases a patch, it will be required to make a new commit updating package.json only changing the version, so my pipeline can also overwrite package-lock.json.

In short, it seems that while package.json uses a feature... package-lock.json prevents that one.

Am I missing something?

  • When hitting `npm install` it will install the versions you have locked. When you hit `npm update` it will fetch the most recent versions tight to the boundaries defined in package.json. It's the same with composer and yarn etc, please read about `semantic versioning`. Usually, you don't commit the lock file anyways. At our company, we commit the lock file only when we use libs where the author does not strictly stick to semantiv versioning and had put API breaks in his(/her) code. – Daniel W. Feb 12 '19 at 14:53
  • @DanFromGermany - Guidance from the `npm` team is explicitly **to** commit the `package-lock.json` file: `npm notice created a lockfile as package-lock.json. You should commit this file.` [More here](https://stackoverflow.com/questions/44206782/do-i-commit-the-package-lock-json-file-created-by-npm-5). Of course, that's just their guidance, which any team is free to disregard for their own reasons. :-) – T.J. Crowder Feb 12 '19 at 14:57
  • 1
    @T.J.Crowder ok, makes sense. In theory, it should work without, only when authors introduce API breaks it should be a problem. But in the real world, this leads to problems sometimes, hence the notice to commit the lock file. – Daniel W. Feb 12 '19 at 15:00
  • 1
    @DanFromGermany - Well, a significant example is actually when they *fix* a bug. Fixing a bug between nifty-lib 0.4.0 and nifty-lib 0.4.1 isn't uncommon. If our team is relying on nifty-lib and I have 0.4.0 and you have 0.4.1 and I'm seeing what looks like a bug in our project when really it's in nifty-lib but you can't replicate it, that can lead to us spinning... If the entire team is using the same version of things, we avoid that issue, track it down to nifty-lib, `npm update`, commit the updated lock, and move on. :-) – T.J. Crowder Feb 12 '19 at 15:03
  • 2
    The short answer is: it makes running `npm update` easier. You are right: if you listed `someLib: "^1.0.0"` in your **package.json**, and the lockfile installs `someLib: "1.0.0"`, you _could_ list that as `someLib: "1.0.0"` in your **package.json** file and it'd have the same effect when running `npm install`. But the point is to communicate to the human developer what version range should work with this application, and make updating easier for that end user. – romellem Feb 12 '19 at 15:12

2 Answers2

5

The point of package-lock.json is to accurately represent the tree as it actually exists at a point in time, so that someone cloning the project gets exactly the same tree you had.

If you want to upgrade that dependency to a newer version, just use npm update and then commit the updated package-lock.json. Other members of your team will get that update as part of the normal process of picking up the latest.

More in the npmjs.com page on package locks.

Let's consider as scenario where you and I are on a team and our project uses nifty-lib, with package.json saying "nifty-lib": "^0.4.0", and we don't share package-lock.json. Perhaps I've been working on the project a couple of months longer than you have and I got nifty-lib v0.4.0 when I installed it. But when you picked it up and installed, you got v0.4.1 (a bugfix update which, sadly, introduced a new bug). At some point, you notice what seems like a bug in our project, but I can't replicate it. We spin in place for a while trying to figure out why it happens to you and not to me. In the end, we realize it's because it's actually a bug in nifty-lib that they introduced in v0.4.1. Hopefully we then get 0.4.2 or something (or if there isn't one, we fix the bug and do a PR, meanwhile rolling back to 0.4.0 across the project).

If we'd been sharing package-lock.json, we wouldn't have spun in place wondering why the problem happened to you and not to me, because you would have had the same version of nifty-lib as me. As part of our normal cycle, we'd do npm update periodically, and if a new bug showed up in our tests, we'd know from the commit history that it was because of a bug in a dependency.

Now, for "me" and "you" read "dev" and "production". :-)

Which is why package-lock.json locks the version, but package.json lets you say "this or better". package-lock.json keeps your team unified on versions, but you can intentionally update with npm update, which shows up in the commit history so you can track down regressions to it.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
2

As I mentioned in a comment above, the short answer is it makes updateing your dependencies easier.

However, another way I like to think about the two files is: package.json is the file the human reads, while package-lock.json is the file the computer reads.

NPM is a package / dependency manager. So, in your package.json file, you write out "these libraries are needed for my library to work." As a feature, you have a range of versions you can list a dependency at. This helps when you run npm update on a specific package. It'll look to see what is the latest version that matches within your *package.json**, and updates you lockfile.

The package-lock.json lockfile is useful because it verbosely describes what your node_modules/ folder looks like so it can be accurately recreated when someone else installs your library. Additionally, since this file is generated automatically, you don't have to worry about maintaining it.

Of course, all of this just happens to be how NPM (and conversely how most package managers) handle this. That is, there isn't a technical reason why we couldn't have one file to describe both the version range that would be allowed when running updates, and a verbose lockfile portion that pins versions to allow for a recreatable dependency tree.

Basically, it is just a convenience. You have one file to succinctly list what dependencies your projects needs. It is readable and easily updatable. The other file, the lockfile, is automatically generated and ensures each npm install gives you the exact same node_modules/ folder as before.

romellem
  • 5,792
  • 1
  • 32
  • 64