# Seeking feedback on dependency management approach

**URL:** https://discuss.gradle.org/t/seeking-feedback-on-dependency-management-approach/9443
**Category:** Help/Discuss
**Created:** [May 2, 2015, 4:33pm UTC](https://discuss.gradle.org/t/seeking-feedback-on-dependency-management-approach/9443 "2015-05-02T16:33:27Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![t\_broyer](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/t_broyer/32/178_2.png) [@t\_broyer](https://discuss.gradle.org/u/t_broyer)
#### Post date: [May 2, 2015, 4:33pm UTC](https://discuss.gradle.org/t/seeking-feedback-on-dependency-management-approach/9443/1 "2015-05-02T16:33:27Z")

</div>

Hi all,

When I first used Gradle a year ago (or so), the first thing I missed (coming from Maven) was a centralized way of defining dependency versions for a (multi-module) project, similar to Maven’s `<dependencyManagement>`. I initially started doing like many and using variables and referencing them from the projects’ `dependencies`; e.g.

```gradle
def fooVersion = '1.0'
ext.libs = [
  someDependency: "com.example:some-dependency:$fooVersion",
  otherDependency: dependencies.create("com.example:other-dependency:$fooVersion") {
      exclude module: 'slf4j-simple'
    },
  yetAnotherDep: ['com.example:yet-another:2.0', 'com.example:some-optional-dep:3.0']
]

dependencies {
  compile libs.someDependency
  test libs.otherDependency
  runtime libs.yetAnotherDep
}

```

For a few months, I’ve started using a _new_ (to me) approach that has the advantage of applying to the `buildscript` too, and managing versions of transitive dependencies (and easy replacement of dependencies). I’d like to know what you think about it.

I create a file called `dependency-management.gradle` where I’ll put all my versions and rules (see below), and I apply it to all projects and their buildscripts with the following snippet in my root `build.gradle`:

```gradle
buildscript {
  apply from: "$rootDir/dependency-management.gradle", to: it
  dependencies {
    classpath 'net.ltgt.gradle:gradle-errorprone-plugin'
  }
}
subprojects*.buildscript {
  apply from: "$rootDir/dependency-management.gradle", to: it
}
allprojects {
  apply from: "$rootDir/dependency-management.gradle"
}

```

Dependencies are then declared without version in my projects (even if they had a version, it would be overwritten), and that also applies to `classpath` dependencies in `buildscript` (as you can see above)

Here’s what my `dependency-management.gradle` looks like:

```gradle
// Note: this script is applied both to Project and ScriptHandler.
// Beware then to only use properties common to both objects,
// specifically 'configurations', 'dependencies', and 'repositories'.

repositories {
  mavenCentral()
}

dependencies.modules {
  module('javax.ws.rs:jsr311-api') {
    replacedBy('org.jboss.resteasy:jaxrs-api')
  }
}

configurations.all {
  exclude group: 'javax.ws.rs', module: 'jsr311-api' // conflicts with org.jboss.resteasy:jaxrs-api

  resolutionStrategy {
    force 'net.ltgt.gradle:gradle-errorprone-plugin:0.0.6'

    // All artifacts in a given groupId and whose artifactId has a given prefix: "foo.bar:baz-*" or "foo.bar:baz_*"
    // (note: matches "foo.bar:baz" and "foo.bar:baz-quux")
    def artifactPrefixInGroup = [
      'com.google.errorprone:error_prone': '2.0.2',
    ]
    // All artifacts in a given groupId: "foo.bar:*"
    def allArtifactsInGroup = [
      'com.google.guava': '18.0',
      'org.slf4j': '1.7.12',
      'commons-logging': [group: 'org.slf4j', name: 'jcl-over-slf4j']
     ]
    // All artifacts whose groupId has a given prefix: "foo.bar.*:*"
    // (note: matches both "foo.bar:baz" and "foo.bar.baz:quux")
    def allArtifactsInGroupPrefix = [
      'com.google.inject': '4.0',
    ]

    def selectReplacement
    selectReplacement = { group, name ->
      def selector = group + ":" + name
      def replacement = artifactPrefixInGroup.findResult {
        if (selector == it.key || selector.startsWith(it.key + "-") || selector.startsWith(it.key + "_")) return it.value
      }
      if (!replacement) {
        replacement = allArtifactsInGroup[group]
        if (!replacement) {
          replacement = allArtifactsInGroupPrefix.findResult {
            if (group == it.key || group.startsWith(it.key + ".")) return it.value
          }
        }
      }
      if (replacement instanceof Map) {
        replacement = selectReplacement(replacement.group, replacement.name)
        assert replacement : "Recursively selecting a replacement for ${group}:${name} returned null"
      } else if (replacement instanceof String) {
        // replacement is a version, keep the same 'group' and 'name'
        replacement = [
            group: group,
            name: name,
            version: replacement
        ]
      }
      return replacement
    }

    eachDependency { DependencyResolveDetails details ->
      def replacement = selectReplacement(details.requested.group, details.requested.name)
      if (replacement) {
        details.useTarget(replacement)
      }
    }
  }
}

```

See here how I ensure that:

- `net.ltgt.gradle:gradle-errorprone-plugin` will have version `0.0.6`
- all error prone dependencies (in group `com.google.errorprone` with name `error_prone` or `error_prone_annotations` for example) will have the same version
- all Guice dependencies (in groups `com.google.inject` and `com.google.inject.extensions`) will have the same version
- similarly, all Slf4j dependencies will have the same version
- all dependencies on `commons-logging:<whatever>` will be replaced with a dependency on `org.slf4,:jcl-over-slf4j` (whose version is then –recursively– resolved to `1.7.12`)

I also used global exclude and replacement rules, all centralized in the same file.

---

<div class="post-metadata">

### Author: ![mark\_vieira](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/mark_vieira/32/5396_2.png) [@mark\_vieira](https://discuss.gradle.org/u/mark_vieira)
#### Post date: [May 2, 2015, 8:38pm UTC](https://discuss.gradle.org/t/seeking-feedback-on-dependency-management-approach/9443/2 "2015-05-02T20:38:16Z")

</div>

You might also want to look at the dependency management plugin from the folks at SpringSource.

> **[spring-gradle-plugins/dependency-management-plugin](https://github.com/spring-gradle-plugins/dependency-management-plugin)**
>
> dependency-management-plugin - A Gradle plugin that provides Maven-like dependency management functionality

Or the dependency recommender plugin from the folks at Netflix.

> **[nebula-plugins/nebula-dependency-recommender-plugin](https://github.com/nebula-plugins/nebula-dependency-recommender-plugin)**
>
> nebula-dependency-recommender-plugin - Leave off version numbers in your dependencies section and have versions recommended by several possible sources.

---

<div class="post-metadata">

### Author: ![t\_broyer](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/t_broyer/32/178_2.png) [@t\_broyer](https://discuss.gradle.org/u/t_broyer)
#### Post date: [May 2, 2015, 9:35pm UTC](https://discuss.gradle.org/t/seeking-feedback-on-dependency-management-approach/9443/3 "2015-05-02T21:35:56Z")

</div>

Oh, I didn’t know about the dependency-management-plugin! At the time I started, the nebula-dependency-recommender wasn’t compatible with the latest version (2.2.1 at the time).

Being plugins, none will manage `buildscript` dependencies (not a big deal though). More importantly, none provide a DSL for replacing a dependency with another (e.g. `commons-logging:commons-logging` with `org.slf4j:jlc-over-slf4j`); that still has to be done using a dependency resolve rule (in the case of commons-logging and jlc-over-slf4j, I suppose a module replacement rule would work too).

---

<div class="post-metadata">

### Author: ![mark\_vieira](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/mark_vieira/32/5396_2.png) [@mark\_vieira](https://discuss.gradle.org/u/mark_vieira)
#### Post date: [May 2, 2015, 9:38pm UTC](https://discuss.gradle.org/t/seeking-feedback-on-dependency-management-approach/9443/4 "2015-05-02T21:38:02Z")

</div>

> [@t\_broyer](#):
>
> More importantly, none provide a DSL for replacing a dependency with another (e.g. commons-logging:commons-logging with org.slf4j:jlc-over-slf4j); that still has to be done using a dependency resolve rule (in the case of commons-logging and jlc-over-slf4j, I suppose a module replacement rule would work too).

Yes, that is correct.
