buildr-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Antoine Toulme <>
Subject Re: Dependencies are naturally version ranges, not single versions
Date Wed, 12 Oct 2016 08:02:13 GMT
Good job Michael, I look forward to using the lock_jar gem in my projects.

Trejkaz, is lock_jar covering your use case?


In the news, the talk of Yarn, the new Javascript dependency manager, and some interesting
comments on HN:
<> <>

Some interesting tidbits:
In fairness to the language and tools, this seems to be more of a cultural problem than anything.
You can do the same kind of version range tricks in typical Java builds, for example (Maven),
but most people hardcode the values to keep builds as deterministic as possible.
For some reason, the JS community seems to prefer just trusting that new versions won't break
anything. Its either very brave of them really (or maybe just foolish).

I hate non-reproducible builds and semver-relaxed dep-of-the-dep issues, but, while a broken
dep fails the build for lots of people (downside), the upside of this is that very quickly
(within hours of a new dep being published) there will be lots angry people complaining about
it on GitHub, and a faulty dep will be typically quickly rolled back / superseded with a patch.
Otherwise, some bugs might be sitting hidden for a long time.

When Maven came about, it’s biggest value-add compared to Ant or makefiles was that it made
builds reproducible.
Using version ranges makes that impossible - but using the approach that Michael borrowed
from Bundler (and is again reused by Yarn from my quick reading) makes the resolution explicit.

> On Oct 9, 2016, at 2:04 AM, Peter Donald <> wrote:
> Hi,
> We have talked about this problem in the past but unfortunately at the time
> no one on the buildr team had the cycles to have a shot at it. Luckily
> someone from the community Michael Guymon, took the idea and ran with it.
> First with
> but this got deprecated in favour of
> I have never used it but have been quite impressed by the idea, design and
> code. I think there is probably a way we could better integrate it with
> Buildr if someone wanted to try and get it as the default/recommended
> mechanisms in buildr but you may also be able to use it as is with little
> to no friction.
> I have CC'ed the developer who may want to advocate for it's use too ;)
> On Sat, Oct 8, 2016 at 4:06 PM, Trejkaz <> wrote:
>> Continuing from
>> Let's suppose that version ranged dependencies don't exist.
>> Let's also suppose that:
>> * Library A depends on Library C 1.0.
>> * Library B depends on Library C 1.1.
>> * The changes from C 1.0 to C 1.1 were done according to semver (all
>> libraries should be doing this), and therefore code written to 1.0
>> will work against 1.1.
>> Now suppose your application wants to depend on both A and B. Now you
>> have a transitive dependency on both C 1.0 and 1.1. If you're using
>> OSGi, this _may_ be okay, though there are cases where it is not - for
>> instance, if two libraries are expected to pass between themselves
>> objects of a class defined in a third. If you're not using OSGi or a
>> similar runtime which allows running two different versions of the
>> same library, now you can't run your code. If you have an application
>> with a large number of dependencies, you end up having to go to
>> multiple libraries and begging them to update to some minor patch
>> release just so that your build can resolve the dependencies.
>> The problem, as I see it, is that A doesn't _really_ depend on C 1.0.
>> By definition, C 1.1 introduced backwards-compatible changes, so A
>> should also work against C 1.1. _If_ A also works against C 1.1, then
>> saying it depends on 1.0 is a lie. As a library developer, I would
>> prefer to specify my dependencies in a way which represents what I
>> actually support, even if the build only runs against one specific
>> version of it. Compiling should be done against the _lowest_ version
>> in the supported range, if it isn't checked against all of them.
>> *Ideally*, tests should be run against all combinations of
>> dependencies. I do see some projects doing this, presumably to be
>> doubly-sure that their dependencies are correct.
>> An an application developer and someone who wants to build the
>> integrated project, I would like all libraries to declare their
>> dependencies accurately, so that the build tool can reliably detect
>> actual conflicts. If B suddenly starts depending on C 2.0, I want to
>> know about that. But today's build tools only give me two
>> unsatisfactory options:
>>  (a) All your versions have to match, which they never do, so your
>> build is always conflicting, and you have to manually inspect other
>> people's code to see whether it will work with a newer version or not.
>>  (b) Allow versions not to match, but when they clash, always pick
>> the latest one, and eventually you will get some NoClassDefFoundError
>> when someone accidentally updates a library which another library
>> actually needed.
>> Accurately specified dependencies should also not make my build
>> unreproducible, as implied by the comment on the ticket. There are
>> multiple reasons why:
>>  1. As long as nobody is specifying open-ended version ranges, it
>> shouldn't even be possible for a new incompatible version to suddenly
>> break the build. You should only be specifying dependencies you have
>> actually tested anyway.
>>  2. If you update a library and the dependency of the updated version
>> was a newer version of some other library, the build didn't break
>> because of version ranges, it broke because you actually changed
>> something.
>>  3. Even problems of specifying open-ended version ranges have been
>> solved by other dependency-resolution systems. For instance, RubyGems
>> keeps a "lock" file which contains the actual versions of everything
>> used last build, so the next build will use those versions until
>> someone actually updates something deliberately. It seems to be
>> working.
>> TX
> -- 
> Cheers,
> Peter Donald

  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message