2

I'm aware that similiar questions have been asked before, but I specifically included the maven tag on this question for a reason. Scenario:

  • Project P has two dependencies, D1-1.2.3 and D2-2.0.0
  • D1-1.2.3 has D2-1.0.0 as a dependency
  • A class C in D1 uses (but does not expose) a class from D2 that has had a breaking change from version 1.0.0 to 2.0.0
  • P uses C

The maven dependency model dictates that since P's pom.xml explicitly states the D2 dependency, the version from the pom will be used. This causes P to break with a linkage error because of the incompatible change of the transitive dependency.

The semver FAQ states that this is a compatible change. It does say "since it does not affect the public API", yet with the scenario I outlined, every update to a dependency implicitly holds a risk of breaking consumers with linkage errors.

Should D1 increase major version? Is this bit of the semver specification simply not apt for maven projects because of its dependency model?

EvenLisle
  • 4,672
  • 3
  • 24
  • 47
  • The question is if your class `C` (the interface of it) needs to be changed based on the transitive change. If you can keep it. Than it's not a breaking change cause your API is stable if you need to change your "interface" this would be a breaking change..and results in a major version change...Apart from that I would explicitly name the version change in your changelog... – khmarbaise Aug 10 '18 at 07:52

1 Answers1

1

Whether the change is compatible or not, in this case, is entirely dependent on how the API consumer uses it, and this is beyond the responsibility of the API developer.

As far as D1's developers are concerned, the public API remained unchanged, and, IMO, it's correct to state that it's not a breaking change.

If the application using D1 also directly uses D2 because it happens to be a compile-scoped dependency, then this is completely the responsibility of the consumer. How? The consumer could anyway exclude the transitive dependency and replace it with a different version, and multiple consumers manage transitive dependencies differently.

As you've stated, most of this is a result of how dependencies work in Maven or Java, but it is sensible to limit the responsibility of API developers to the public API.

ernest_k
  • 44,416
  • 5
  • 53
  • 99
  • "The consumer could anyway exclude the transitive dependency" - That wouldn't help though, would it? A method exposed in `D1`'s public API uses (and has been compiled against) the early version of `D2`. The reason for the linkage error in the first place is that `P` has a newer version (that changed incompatibly) in its pom, not the other way around – EvenLisle Aug 10 '18 at 08:28
  • @EvenLisle then the problem is more basic than versioning here... the implementation details have leaked into the public API. If that's accepted/acceptable, then I would agree with you that a newer version would be appropriate. But that's unmanageable. – ernest_k Aug 10 '18 at 08:35
  • "As far as D1's developers are concerned, the public API remained unchanged" - I both agree and disagree. I agree they can't possibly know whether something like this would occur. But what I'm getting at is that - in the maven case - we can't ever assume that updating a dependency with a breaking change won't break the public API – EvenLisle Aug 10 '18 at 08:37
  • @EvenLisle Your question didn't mention that components from the transitive dependency featured in the public API. See my previous comment. – ernest_k Aug 10 '18 at 08:39
  • "Your question didn't mention that components from the transitive dependency featured in the public API" - yes it did: "A class `C` in `D1` uses (but does not expose) a class from `D2` that has had a breaking change from version `1.0.0` to `2.0.0`". Even though it's not exposed (i.e. returned to the consumer) its usage still breaks consumers of `D1` – EvenLisle Aug 10 '18 at 08:43
  • I've created an issue suggesting a change in wording so the semver specification doesn't make assumptions about dependency models: https://github.com/semver/semver/issues/456 – EvenLisle Aug 10 '18 at 08:44
  • If it doesn't expose the class in the public API, then I'd still claim that it isn't a breaking change. If you don't have to refactor/recompile your code, then it is not a breaking change. – ernest_k Aug 10 '18 at 08:50
  • I concede that this is likely way out of the scope of semver itself. Accepted & upvoted your answer – EvenLisle Aug 10 '18 at 09:01
  • Since when are the manifests included in your package not part of the public API(s) contained in that package? It's one thing to slap a version label on an API and quite another to put one on the package that contains that API and other content. Make a change to that package that breaks my build, you better bump its major version number or I am going to file a bug against you and possibly stop using your product(s). I need to count on being able to automatically take up bug fixes without having to spend hours investigating/fixing build breaks. – jwdonahue Aug 18 '18 at 03:49
  • When the environment provides for side-by-side deployments of API's/packages, most transient dependencies are satisfied. In environments that do not have side-by-side deployments, transient dependencies must be taken into consideration when you update your packages. – jwdonahue Aug 18 '18 at 03:52
  • @jwdonahue you have an interesting view. Do you include your own dependency tree in your manifest files? And what about shaded jars? And do you realize that whether that is effectively a "breaking" change in this case depends solely on how the transitive dependency happens to be used by the library user? – ernest_k Aug 18 '18 at 07:08
  • @ernest_k, I don't want to know what a shaded jar is. I try to avoid java because my experience with it has shown that it promotes unsound practices. When you produce a product that contains a diamond point dependency, and side-by-side deployment/execution of multiple versions of the same components is not the norm, then you are purveying time-bombs. Unless I misread the OP's problem statement, the latest version of P now depends on two different versions of D2 where one was previously required. Unless java or maven provides side-by-side deployments, that change in P is a breaking change. – jwdonahue Aug 18 '18 at 17:17
  • @jwdonahue it's likely that you misunderstand how maven transitive dependencies work. But the fact is that the api developer did not change the public API. The client chose to use the transitive dependency directly, so it's their responsibility to deal with the issue caused by the upgrade. Your opinion about Java is intriguing, to say the least. Have you used maven before? – ernest_k Aug 18 '18 at 17:31
  • I am basically saying that stating that X depends on Y is weak, if the full closure of the dependency tree is not readily available, and the consumer cannot easily resort to side-by-side deployment and execution. By weak, I mean insecure and totally unreliable. Hence the exploitable web we have today. – jwdonahue Aug 18 '18 at 17:32
  • @ernest_k, I think it's likely I do misunderstand this particular problem, given my blind-spots in both java and maven. – jwdonahue Aug 18 '18 at 17:42
  • 1
    Revisiting this because the OP has an [active thread on the semver project on github](https://github.com/semver/semver/issues/456#issue-349426048). – jwdonahue Sep 20 '18 at 17:15
  • @jwdonahue Maybe you guys can share your opinion/resolution as comments or answers to this question (whenever you resolve that issue) – ernest_k Sep 20 '18 at 17:18
  • 1
    @ernest_k, a good plan. I was going to tweak one of my responses on the issue thread and post it, but now I think I'll wait for resolution on that thread. – jwdonahue Sep 20 '18 at 17:21