Comment by moritzwarhier
1 day ago
> The problem with NPM isn't any one young package. The problem with the NPM is that any time you run 'npm install', you download potentially thousands of packages, and you get the most recent patch release from all of them.
Isn't this simply wrong?
Last I checked, lock files work. They didn't for a long time, until a couple of years ago, as far as I know.
If you delete your lock file or explicitly run a package upgrade, sure, you get the latest versions compatible with your semver ranges.
> Installing one 1-day-old NPM package to forever avoid day 1 releases of thousands of packages seems like a worthwhile trade.
If you want to be extra sure, you can simply not use semver ranges in your package.json, or only for select packages.
As far as I know, this is recommended anyway.
Lockfiles work if you combine them with version pinning (exact version, no semver), or always run `npm install ci` unless you’re intentionally attempting to update your packages.
I’ve always preferred exact versions because I’d rather updates be opt-in rather than an opt-out footgun. Otherwise any new dev to the project might accidentally pull some new version of a package that satisfies the semver requirement but modifies the lockfile, then they’ll check it into the code, and it’s another thing to fix at review time… there’s just a lot less friction if you use exact versions. It makes hermetic/reproducible builds and static dependency analysis easier, too.
Of course you need some update hygiene, preferably via an automated bot that opens PRs and runs tests. Renovate works well.
(btw, this same issue occurs with Docker base images; it’s better to base images on the sha256sum of the target image rather than a floating tag. Renovate can update those too.)
Doesn't NPM only respect lock files when you run 'npm ci'? I thought 'npm install' just used the constraints in package.json
You are right that 'npm install' can upgrade versions even when a lock file is present, but AFAIK this should only happen it the lock file is not compatible with the package.json. I haven't seen it in a long time, and AFAIK it can't happen without you changing the package.json.
But yes, it's a reason to pin dependencies and use npm ci / yarn immutable etc.
Updates of transitive dependencies are afaik not automatically installed when there is a working lock file: this is the thing that changed some versions ago I think (I mixed up Node and npm versions in my initial comment).
So yes, to be sure that you never install anything else, it's best to use 'npm ci' or 'yarn install --immutable', which will fail if the lock file is broken or not present.
But 'npm install' does not install the latest patch release compatible with your package.json with precedence over the lockfile.
What it does do is upgrade if you edit the version range by hand to be incompatible with the lock file, e.g. increase major version of a package.
But if you have, say, Typescript ^5 in your package.json, but 5.4 in your lock file, 'npm install' won't upgrade it.
https://docs.npmjs.com/cli/v11/commands/npm-install
> If the package has a package-lock, or an npm shrinkwrap file, or a yarn lock file, the installation of dependencies will be driven by that, respecting the following order of precedence:
> npm-shrinkwrap.json
> package-lock.json
> yarn.lock
'npm ci' and friends are safer as they will always fail when they can't install from lock file without any conflicts or changes, that's correct.
Don't know how other package managers behave in this regard, except for yarn and pnpm.
PHP composer AFAIK behaves similar to npm?