Search code examples
mavendependencies

Maven: explicitly include dependency version or exclude unwanted versions where they’re coming in as a transitive dependency?


Is it a better approach to explicitly include a dependency version (that is already being included via another dependency), or exclude the unwanted versions where they’re coming in as a transitive dependency?

Example:

There’s a library: initech-ibrary of v2.0.5, which has the following dependencies:

<dependency>
    <groupId>com.initech</groupId>
    <artifactId>printer-tools</artifactId>
    <version>1.0.2</version>
</dependency>

<dependency>
    <groupId>com.initech</groupId>
    <artifactId>penny-pinching</artifactId>
    <version>3.0.11</version>
</dependency>

<dependency>
    <groupId>com.initech</groupId>
    <artifactId>tps-reports</artifactId>
    <version>3.4.9</version>
</dependency>

Approach 1

An application has the following dependencies declared

<dependency>
    <groupId>com.initech</groupId>
    <artifactId>initech-ibrary</artifactId>
    <version>2.0.5</version>
</dependency>

<dependency>
    <groupId>com.initech</groupId>
    <artifactId>other-tools</artifactId>
    <version>2.0.10</version>
</dependency>

<dependency>
    <groupId>com.initech</groupId>
    <artifactId>printer-tools</artifactId>
    <version>1.0.2</version>
</dependency>

<dependency>
    <groupId>com.initech</groupId>
    <artifactId>penny-pinching</artifactId>
    <version>3.0.11</version>
</dependency>

<dependency>
    <groupId>com.initech</groupId>
    <artifactId>tps-reports</artifactId>
    <version>3.4.9</version>
</dependency>

These specific versions (for printer-tools, penny-pinching and tps-reports) are not defined due to incompatibilities with the application or needing a specific older version for a feature or anything like that - they’re just there to prevent other versions being picked up transitively because other-tools has a dependency on an older version of initech-ibrary than the currently declared v2.0.5.

Approach 2

<dependency>
    <groupId>com.initech</groupId>
    <artifactId>initech-ibrary</artifactId>
    <version>2.0.5</version>
</dependency>

<dependency>
    <groupId>com.initech</groupId>
    <artifactId>other-tools</artifactId>
    <version>2.0.10</version>
    <exclusions>
        <exclusion>
            <groupId>com.initech</groupId>
            <artifactId>tps-reports</artifactId>
        </exclusion>
    </exclusions>
</dependency>

This approach specifically excludes tps-reports from other-tools because it has a dependency on an older version of initech-ibrary which is pulling in an older version of tps-reports than what is in initech-ibrary v2.0.5, and that older version is being used instead.

Approach 1 has the problem that when the version of initech-library gets updated, then all the other dependencies need to be looked up and also updated but it has the benefit of not having to worry about the wrong versions being pulled in, whereas Approach 2 simplifies the dependencies, but requires the developer to be more aware of any version changes and future dependency additions.

What are the other possible problems that could occur from each approach?

Thanks!


Solution

  • There is a third possibility, which I prefer:

    Define the version of the dependency in the <dependencyManagement> section of your POM. This means that you get the (recent) versions that you want, but if the transitive dependency is no longer in the dependency tree (after an update), the dependencyManagement just does nothing, so you do not pollute your artifact with unnecessary dependencies. Furthermore, if you read the POM, it is clear that these versions are not your dependencies, but transitive ones.