Yes
The answer is yes, absolutely always commit your lockfile to git.
Analogy to git hashes
If you use git, you should use a lockfile because they serve the same purpose:
- a git hash guarantees stability for the contents of files in a git repo.
- a lockfile guarantees stability for the contents of node_modules.
Because...
- files in a git repo may change over time, but a git hash refers to an exact snapshot of files.
- npm packages in the npm registry may change over time, but a lockfile refers to an exact snapshot of dependencies.
From the package managers themselves
Package manager vendors clearly say you should commit the lockfile.
npm
It is highly recommended you commit the generated package lock to source control ...
https://docs.npmjs.com/cli/v6/configuring-npm/package-locks
When npm
installs without a lockfile, it tells you clearly to commit the lockfile:
npm notice created a lockfile as package-lock.json. You should commit this file.
yarn
Lockfiles should be committed on all projects
https://classic.yarnpkg.com/blog/2016/11/24/lockfiles-for-all/
pnpm
You should always commit the lockfile (pnpm-lock.yaml
).
https://pnpm.io/git
Reasons
Consistency
A single commit should be the same forever, and its build output should not change over time.
From npm:
[a lockfile] 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.
From yarn:
If you don’t store which version you ended up installing, someone could be installing the same set of dependencies and end up with different versions depending on when they installed. This can lead to “Works On My Machine” problems and should be avoided.
Traceability of changes
When something changes, you want git to track that change.
From npm:
[with a lockfile] the diffs from [installation] 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.
Stability and security
Avoid introducing bugs and vulnerabilities.
From yarn:
Since package authors are people and they can make mistake, it’s possible for them to publish an accidental breaking change in a minor or patch version. If you install this breaking change when you don’t intend to it could have bad consequences like breaking your app in production.
If the package author is either malicious or is attacked by someone malicious and a bad version is published, you do not want that code to end up running without you knowing about it.
Common Objections
"There's no point"
This is an "Argument from Ignorance" which is a logical fallacy. In other words, "I don't know the reason, so there is none".
"It's generated"
If this file is autogenerated, they why should we commit it? Why can't npm generate it again according to my package.json.
from comment
Response: being generated is not a flaw. Git commit hashes are generated; should we not use git? The truth is that the lockfile is not deterministically generated from package.json, because it is susceptible to time and the state of packages in the npm registry. It is a snapshot, for stability.
"It causes merge conflicts"
It's a burden to resolve merge conflicts in checked-in lockfiles.
From npm:
As of npm@5.7.0
, these conflicts can be resolved by manually fixing any package.json
conflicts, and then running npm install [--package-lock-only]
again.
From yarn:
when there’s a merge conflict in the lockfile, Yarn will automatically handle the conflict resolution for you upon running yarn install
.
https://engineering.fb.com/2017/09/07/web/announcing-yarn-1-0/
From pnpm:
pnpm can automatically resolve merge conflicts in pnpm-lock.yaml
. If you have conflicts, just run pnpm install
and commit the changes.
So all package managers resolve lockfile merge conflicts automatically. This may not be the case in older versions, but it is the case now.
The only time this fails is if the package.json
itself has conflicts, because you can't install from an invalid package.json
. You must resolve those conflicts manually as you would have to do anyway.
"The merge conflicts interfere with PRs and MRs"
Using lock files greatly increases the chance that merging one PR will result in a second PR becoming conflicted with the base branch.
https://docs.renovatebot.com/noise-reduction/#lock-file-considerations
This is true. Git providers (GitHub, GitLab, etc) don't automatically resolve lockfile conflicts, so this may add a burden to merging. However when weighing this con, understand that lockfiles do not normally change; they only change when package.json
deps change, or when a developer specifically changes the file or the installed node_modules
deps.
"It makes diff noise"
If diff tools show lockfile diffs, it's a lot of noise.
This is true, however it's a tooling problem which many tools can handle gracefully (such as auto-minimizing, paging, or virtual scrolling). If you don't want to see the lockfile diff at all, try git diff -- . ':(exclude)yarn.lock'
, or alternatively mark the file as binary in .gitattributes
(however you won't see its diff, if that matters to you).
"Exact versions are good enough"
Why not just hardcode dependency version by getting rid of carets and tildes (^ and ~)?
comment
The idea is that not using ranges in your package.json
's dependency semver expressions accomplishes the same thing as having a lockfile.
This is false. Even if you specify exact versions, your dependencies have their own dependencies, which may use ranges for their versions, not exact versions. So this doesn't end up locking the whole dependency tree, only the top of it.
"Lockfiles for apps, no lockfiles for libraries"
Examples of this objection:
The sentiment is that libraries need to react to bleeding edge deps, and that not having lockfiles supports this.
From yarn:
Some have wondered why libraries should use lockfiles at all ... that using lockfiles when developing libraries creates a false sense of security since your users could be installing different versions than you.
This seems to logically makes sense, but let’s dive deeper into the problem.
The yarn article goes into depth to dispel this objection. Please read it.
A common error in this argument is the thought that if you don't commit the lockfile, it doesn't exist. In reality, it's still there on your machine, locking your dependencies. The situation is not improved by gitignoring the lockfile.
If a library maintainer wishes to continually test for compatibility, then they should delete their lockfile (whether the lockfile is checked in or not!) before installing and building their library. The only difference with a checked-in lockfile is that you have a persistent record of the state of node_modules when this happened, so it can be reproduced in the future.
There are bots like greenkeeper and renovate-bot for this. Greenkeeper advocates for checking in lockfiles (Greenkeeper and Lockfiles: A match made in heaven) and renovate-bot expresses no opinion but does commit lockfiles if present.
"Lockfiles are generated differently by different systems"
This is a claim mentioned (e.g. here): that different OSes generate different lockfile contents. If this is the case, this is a bug.
However it is possible that different versions of npm
(or any package manager) may produce different lockfile output. I have not confirmed this, but hypothetically if so, it is a small price to pay for stability. To workaround this, contributors will need to switch their package manager version, by using a tool like nvm.
"Lockfiles can be a security risk"
See Snyk - Why npm lockfiles can be a security blindspot for injecting malicious modules
This is a real risk. A public project with a lockfile can receive a malicious PR with lockfile contents that could compromise a maintainer's machine once the branch is pulled and installed.
Defend against this with CI checks like lockfile-lint or simply npm ci
or yarn --immutable
(yarn --frozen-lockfile
on Yarn 1), and perhaps setting ignore-scripts
locally in your npmrc
.
This risk is present whenever you install a package with untrusted code.
In Conclusion
Always commit the lockfile.