> > They do. The fact that they create further (solvable) challenges does not > mean that they are not a solution to the initial problem. > > The second point is also incorrect - indeed, this is exactly the point of > using a range rather than a point version. If I depend on a range "[1.4, > 2.0)", this is because I need a *feature* that was released in version 1.4 > of the dependency. >
This is an arbitrary interpretation of a range. In practice people use them for very different purposes. If, when you write "[1.4,2.0)", you assume that you need a feature of 1.4, this is already an assumption. Most people use 1.4 as the baseline because _this is the latest version available when I started_. They also _assume_ that anything from 1.4 would work. In practice this is rarely the case. There are bugs introduced, there are binary incompatibilities (despite semantic versioning). Ranges are a _convenience_, but certainly not an answer. Ranges + locking are better, because you _have to_ test, but they don't account for the environment either (say, my app depends on servlet-api, which version should you use? It should be _strictly_ what the runtime environment will give). > > Well what does it mean when you write "compile 'foo:bar:1.0.4'" in your > build.gradle file? It means you have compiled against that version of the > API, and your module will be compatible with that version up to the next > breaking change (2.0 if the dependency is using semver). > > In Gradle you'd not use `compile` anymore. You would use: api 'foo:bar:1.0.4' for an API dependency, one that is exposed by your very own library (mostly maps to "requires transitive") and you'd use: implementation 'foo:baz:2.1.4' for an implementation dependency, that is _not_ exposed by your API (mostly maps to "requires"). And if a transitive dependency needs a different version, we have strategies to select a best match, or fail. > If you wanted your module to be compatible with 1.0.3 then you would have > compiled against 1.0.3. Unless you do that, there is no way for any tooling > to infer that you are indeed compatible with 1.0.3. > That's again an over simplification. Real world apps have different problems. You may say "1.0.3", because 1.0.2 had a bug. Maybe your app didn't even depend on the faulty behavior, but because it had a bug, you upgraded. And, maybe one of your dependencies actually required 1.0.2 because 1.0.3 introduced a regression. So you want to be able to downgrade dependencies. Gradle makes it possible. There's a big difference between the "ideal world", and the world we live in.