DEV Community

loading...

Explain package-lock.json like I am five

ankitutekar profile image Ankit Utekar ・1 min read

What is the point of having same dependency tree ? And how does dependency tree actually work ? Should I commit package-lock.json every time I add new package ? How is it related to symbols (e.g. ^ ) that are put before package versions in package.json ?

Discussion

pic
Editor guide
Collapse
itsasine profile image
ItsASine (Kayla)

There are four different ways you could say you need to have ice cream:

  • I want ice cream (ice-cream: *)
  • I want chocolate ice cream (ice-cream: ^1.0.0)
  • I want chocolate ice cream with sprinkles (ice-cream: ~1.0.0)
  • I want chocolate ice cream with chocolate sprinkles (ice-cream: 1.0.0)

Regardless, when you get your ice cream, it's chocolate with chocolate sprinkles. You're now locked into that. If you want your friend to have ice cream, too, you can give him the lock so you're both enjoying the same ice cream. If it doesn't matter exactly having the same ice cream in this moment, though, you can just give him those instructions I called out above rather than also giving him the lock.

Now that your friend is getting ice cream without a lock, he might get:

  • Vanilla ice cream (ice-cream: *)
  • Chocolate ice cream with peanuts (ice-cream: ^1.0.0)
  • Chocolate ice cream with rainbow sprinkles (ice-cream: ~1.0.0)
  • Chocolate ice cream with chocolate sprinkles (ice-cream: 1.0.0)

If we're cool with him maybe working with different ice cream than you, then awesome! If not, if maybe he's a dude who is trying to figure out whether or not to invest millions into your ice cream and he got something totally different, then you should probably give him the lock file to make sure you guys are on the same page.

Collapse
ankitutekar profile image
Ankit Utekar Author

Say I have specified ice-cream: ^ 1.0.0 in package.json and given him the lock file. Now ice-cream 1.1.0 is available, shouldn't he be getting 1.1.0 because my package.json has ^ symbol specified?

Collapse
itsasine profile image
ItsASine (Kayla)

Keeping the ice cream metaphor because I think I'm clever:

When you're starting your ice cream business, it may not actually matter what kind of ice cream. You're focused on getting marketing and a retail location and stuff like that, so strawberry ice cream wouldn't kill things right now.

But once you open your business, your customers need reliability. Now that they've seen chocolate ice cream with chocolate sprinkles on the menu, they want to keep coming back for that.

That's why there would be a difference in dev dependencies, application dependencies, but also wanting to have a specific version actually deployed every time. The app just needs ice cream, you developing might need chocolate ice cream to test consistently, but now the server should always build with chocolate ice cream with chocolate sprinkles so no quirks from peanut allergies make it into production.

Collapse
rhymes profile image
rhymes

No, it reads the package-lock.json.

package.json is for you as the developer
package-lock.json is for me (or the server) as the installer

The package.json will be considered only if the lock is missing, hence the reason why they invented the lock, because the package.json is not enough to guarantee repeatability

Collapse
rhymes profile image
rhymes

What is the point of having same dependency tree ?

The lock file main purpose is repeatability of the installs.
If you have it you can be sure the packages listed there are the same that are going to be installed on your production application.

If you only have the package.json you will probably end up with slightly different versions for the same packages.

Should I commit package-lock.json every time I add new package ?

Yes

How is it related to symbols (e.g. ^ ) that are put before package versions in package.json ?

When you specify your dependencies you have multiple ways to specify a dependency. You can list the exact version (but it means you have to change both package.json and the lock file every time there's a new version) or you can use semver which allows you to be a little more elastic. ^1.2.3 for example means install all the 1.x.x future versions because the agreement says: "1 is the major version, 2 is the minor version, 3 is the patch version". So the developer likely won't release incompatible software until the version goes from 1.x.x to 2.x.x, and such version won't be intercepted by your initial rule ^1.2.3. If instead you only want bugfixes (the last digit) you can use ~1.2.3 which means install all the 1.2.x versions.

Collapse
ankitutekar profile image
Ankit Utekar Author

Thank you for your response.
I'm still kinda confused.

  1. Can I have only package.json without any symbol specified(i.e. exact version) so that same package version is installed everywhere ?
  2. Say I have specified awesome-package: ^ 1.2.3 in package.json and accordingly it's dependency tree is added in package-lock.json, will it install awesome-package-1.3.0 when available ? A) If yes, will it update package-lock.json as well ? If so then we are not using exact same version everywhere even though we are using package-lock.json, right ? B) If no, then what's the point of specifying those symbols if updates are not getting installed ?
Collapse
rhymes profile image
rhymes

Can I have only package.json without any symbol specified(i.e. exact version) so that same package version is installed everywhere ?

To have the same package AND the same version installed everywhere you need to specify the version, for example

{"dependencies": {"hello": "1.2.3"}}

In theory this guarantees that your hello package is installed with the same version everywhere. Unfortunately it doesn't guarantee that any other library that hello uses as a dependency will be installed with the same version. For this, you need the lock file

A) If yes, will it update package-lock.json as well

Yes but only when you add a package. If the version you allow points to 1.3.0 the lock file will be updated when you run npm install.

If so then we are not using exact same version everywhere even though we are using package-lock.json,

Yes, you are. When you install (and not add) a package, the package.json is totally bypassed. What npm does is read the package-lock.json and install the exact versions specified there

Thread Thread
ankitutekar profile image
Ankit Utekar Author

Oh.. Thanks for the clarification 😊

Collapse
terabytetiger profile image
Tyler V. (he/him)

I don't think I'm able to answer everything, but here's at least some points:

  • The ^ in ^3.0.0 indicates that the package needs to be at least version 3.0.0. So having version 2.9.9 installed on the machine would require an update to version 3.0.0 or newer

  • The purpose of package-lock.json is to outline the list of version requirements for each package. This is useful for 2 reasons:

    • If a new feature is released in fancyTool.js v. 1.1.0 that you implement, you wouldn't want users to install v. 1.0.0. and wonder why it isn't working.
    • If the syntax changes dramatically from v. 1.0.0 to v. 2.0.0, you want to be able to require that v. 1.0.0 be installed instead of v. 2.0.0 (which would in turn break your program).
Collapse
ankitutekar profile image
Ankit Utekar Author

Then why to have both package-lock.json and those symbols in package.json ? Purpose of both of these things is same right ? How do they work together ?

Collapse
terabytetiger profile image
Tyler V. (he/him)

The symbols are part of the package-lock.json structure. Each package independently can be assigned either a specific version or a minimum version that is needed. i.e.)

  "cookie": "0.3.1",
  "cookie-signature": "1.0.6",
  "debug": "2.6.8",
  "depd": "^1.1.1",

In this example, cookie, cookie-signature, and debug will install their specific versions and depd needs at least 1.1.1, but would also accept something like 2.0.0 or 1.2.1

Collapse
gmartigny profile image
Guillaume Martigny

A small thing to add here regarding lock files is speed. If your repo don't have a lock file, NPM (or any package manager) check all existing version of your dependencies and install the ones matching what you asked for. The same process if repeated for all sub-dependencies and so on.

Lock file remove this roundabout, it don't need to check to know "who need what" or "what version match this".

I just test on a project:

$ rm -rf node_modules/ package-lock.json
$ npm install
> added 1392 packages from 555 contributors, updated 2 packages and audited 52426 packages in 140.629s
$ rm -rf node_modules/
$ npm install
> added 1394 packages from 556 contributors and audited 52426 packages in 19.795s
Collapse
steelwolf180 profile image
Max Ong Zong Bao

Package-lock.json is the building instructions that comes with a Lego model.

Each package represents the different shapes or colour for a part that you eventually use to build a Lego model out of it.

Without a package-lock.json, it is like you are spreading it around and mixing with different Lego parts that do not come with the box.

By using symbols, you allow different types of the part to build your Lego model.

Which each part could be switched from an older Lego model you had brought previously or a new box of Lego model you had brought just now.

Collapse
jamonjamon profile image
Jaimie Carter

I just got one of these. So, basically, I do nothing? Include it in git commits, and that's it?

Collapse
ankitutekar profile image
Ankit Utekar Author

Yes, make sure it's committed along with package.json changes.

Collapse
jamonjamon profile image