>1. commons-compress 2 must not be compatible with v1 by commons rule (did
>we miss it? the rule is to change the package to "2")

Romain, It sounds like you’re referring to an Apache Commons convention
(e.g., commons-lang -> commons-lang3, package rename to allow side-by-side).

That can be useful for incompatible redesigns, but for a change like “keep
the same APIs, just split one JAR into multiple modules,”
forcing package renames places a significant burden on consumers.

More broadly, preserving backward compatibility lets users upgrade without
code churn.
FWIW, semver.org does not require “v2 must be incompatible with v1”

>2. in your pom you can always exclude from a dep a transitive dep and
enforce it explicitly

Could you please clarify with a concrete example?
If you mean adding exclusions everywhere and then adding an explicit
dependency on commons-compress,
that’s exactly the kind of manual fix I’m hoping to avoid.

Here's the pom for the application:
https://github.com/vlsi/jarsplit/blob/5e3fa1ecd3f26a2116aa83a92e85b69560a597f6/lib-user-maven4/pom.xml

Consider a novice user who depends on both lib-uses-v1 and lib-uses-v2.
Today Maven resolves commons-compress to 1.0.0, and they get a
NoSuchMethodError.
The recommended fix becomes: “exclude commons-compress (which you never
used directly) from multiple places
and then add it back explicitly at the top level; and later remember to
remove the workaround once transitive dependencies stabilize.”

I think the build tool can do better than that.

Here’s the kind of change I’m trying not to require users to hand-craft:

--- a/lib-user-maven4/pom.xml
+++ b/lib-user-maven4/pom.xml
@@ -6,15 +6,32 @@
     <artifactId>app</artifactId>
     <version>1.0.0</version>
     <dependencies>
+        <dependency>
+            <groupId>org.example</groupId>
+            <artifactId>commons-compress</artifactId>
+            <version>2.0.0</version>
+        </dependency>
         <dependency>
             <groupId>org.example</groupId>
             <artifactId>lib-uses-v1</artifactId>
             <version>1.0.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.example</groupId>
+                    <artifactId>commons-compress</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
             <groupId>org.example</groupId>
             <artifactId>lib-uses-v2</artifactId>
             <version>2.0.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.example</groupId>
+                    <artifactId>commons-compress</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
     </dependencies>
     <build>

This is brittle and requires users to know internal transitive details they
shouldn’t have to care about.

Note: the app does not use commons-compress, nor does it know there's
commons-compress somewhere down the road.

Yes, I could add a top-level dependencyManagement:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.example</groupId>
                <artifactId>commons-compress</artifactId>
                <version>2.0.0</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

…but that’s precisely the point of my original request: the same
dependencyManagement already exists in commons-compress-core:2.0.0,
and I’d like Maven to honor it transitively so end users don’t need to
duplicate it in their root POMs
(which may not even “see” all the relevant transitive edges).

>3. if 2 is bothering the common trick is to do a module with packaging=pom
>doing the exclude+explicit dep and use this instead of the 3rd party
...

If by this you mean creating a wrapper module (a BOM or a “starter”) that
either (a) manages versions or (b) carries curated dependencies plus
exclusions,
then yes, that’s a known workaround.
It works, but it pushes complexity onto every consumer to adopt yet another
indirection layer.

My goal is to avoid forcing all library users to create or adopt wrapper
modules just to get a safe, convergent outcome.

I would much prefer Maven to honor the dependencyManagement present in
transitive POMs (e.g., commons-compress:2.0.0 managing its split modules).
That would let the ecosystem migrate from single-JAR to multi-JAR without
package renames and without end-user boilerplate

>4. maven enforcer or any other plugin to prevent

the usage of v1 (or v2 aggregator module) anywhere
Enforcer is helpful for guardrails, agreed. I still want Maven to resolve
to 2.0.0 automatically in the simple case where an application depends
on two libraries that require different major lines of the same component.

Enforcer can catch regressions; it shouldn’t be the primary mechanism to
coax Maven into a workable resolution.

Vladimir

Reply via email to