Remix.run Logo
Karrot_Kream 4 days ago

When I used to lead a Maven project I'd take dependency-upgrade tickets that would just be me bumping up a package version then whack-a-moling overrides and editing callsites to make dependency resolution not pull up conflicting packages until it worked. Probably lost a few days a quarter that way. I even remember the playlists I used to listen to when I was doing that work (:

Lockfiles are great.

RobRivera 4 days ago | parent | next [-]

> I even remember the playlists I used to listen to when I was doing that work (:

Im a big fan of anything Aphex Twin for these type of sessions.

yawaramin 4 days ago | parent | prev [-]

How do lockfiles solve this problem? You would still have dependency-upgrade tickets and whack-a-mole, no? Or do you just never upgrade anything?

hyperpape 4 days ago | parent | next [-]

I think the difference is that since libraries do not specify version ranges, you must manually override their choices to find a compatible set of dependencies.

The solution is version ranges, but this then necessitates lockfiles, to avoid the problem of uncontrolled upgrades.

That said, there's an option that uses version ranges, and avoids nondeterminism without lockfiles: https://matklad.github.io/2024/12/24/minimal-version-selecti....

Note: maven technically allows version ranges, but they're rarely used.

chowells 4 days ago | parent | prev | next [-]

The difference is that the data is centralized with a single source of truth, and you have tools for working with it automatically. It doesn't mean lockfiles are cheap to update, but it does mean it's a much more streamlined process when it's time.

yawaramin 4 days ago | parent [-]

The data is also centralized without lockfiles though...it's in the package spec file itself, where all version can be upgraded together. If you are saying the only difference is some tooling for automation, that's a temporary problem, not a fundamental one.

chowells 4 days ago | parent | next [-]

By "centralized" I mean a single file that contains all transitive dependency version information and nothing else. This is very different than having to recursively examine tiny portions of your dependencies' package specs recursively to find all transitive dependencies.

growse 4 days ago | parent | prev [-]

Without a lock file your transitive dependencies are.... by definition not centralised?

Or have I misunderstood?

Muromec 4 days ago | parent | prev [-]

you press a button which triggers a pipeline which does npm update on all root dependencies and produces a new lockfile, then creates a PR you need to approve. creating a PR triggers running all the tests you bothered to write to also flag things that didn't go well automagically.

yawaramin 4 days ago | parent [-]

Is this a joke? What's the difference between this and pressing a button which triggers a pipeline which does npm update on all root dependencies and produces a new package.json file which you approve in a PR? Is the only difference that some convenient tooling already exists so you don't have to update each root dependency by hand?

crote 4 days ago | parent | next [-]

A package spec defines your desires for your dependencies, a lockfile defines a resolution of those desires.

For example, as a developer I want Spring to stay on 6.3.x and not suddenly jump to 6.4 - as that is likely to break stuff. I do not care whether I get 6.3.1 or 6.3.6, as they are quite unlikely to cause issues. I do not care the slightest what version of libfoobar I get 5 dependencies down the line.

However, I do not want packages to suddenly change versions between different CI runs. Breakage due to minor version bumps are unlikely, but they will happen. That kind of stuff is only going to cause noise when a rebuild of a PR causes it to break with zero lines changed, so you only want version upgrades to happen specifically when you ask for them. On top of that there's the risk of supply chain attacks, where pulling in the absolute latest version of every single package isn't exactly a great idea.

The package spec allows me to define "spring at 6.3.x", the lockfile stores that we are currently using "spring 6.3.5, libfoobar 1.2.3". I ask it to look for upgrades, it resolves to "spring 6.3.6, libfoobar 1.4.0". There's also a "spring 6.4.0" available, but the spec says that we aren't interested so it gets ignored. All tests pass, it gets merged, and we'll stay at those versions until we explicitly ask for another upgrade.

The whole flow exists for things which aren't root dependencies. The major versions of those are trivial to keep track of manually, and you'll only have a handful of them. It's all the minor versions and downstream dependencies which are the issue: tracking them all manually is a nightmare, and picking whatever happens to be the latest version at time-of-build is a massive no-no.

Muromec 4 days ago | parent | prev [-]

The difference is transitive dependences with ranges, which results in less frequent updates of direct dependences. And tooling of course too