Dependency Management in Gradle

There is one super popular thing I never did/do in my Gradle projects:

ext {
    lib_version = "1.2.3"
}

dependencies {
    compile "org.corp:lib:$lib_version"
}

Why not?

I know that this approach was/is popular among Maven projects.
But I always preferred the dependencyManagement section of a parent POM because… it is standartized.

When you switch between projects and want to change a version of dependency, you know where you look at. Heck! You can even write an XSLT and change it, in any project you have!

With properties, however, there is no standard way to name them.
$lib_version?
$org.corp.lib?
$org.corp.lib_version?
$lead_engineer_told_me_to_extract_a_property_so_I_did?…

The same applies to Gradle! But, in Gradle, you have to remember that these are variables, with their own syntax limitations.

And it does not help IDEs too! compile "org.corp:lib:$lib_version" - from where does $lib_version come from? Is it defined somewhere in the code? Or maybe that’s a user-provided property?

The IDEs have to know where to look for them (due to the underlying magic), and remember about the local variables too!

And god help me if I decided to rename one of these ext variables…

How to: declarative way

While Gradle DSL is imperative, I still like declarative-ish code.

If you’re using Spring Boot, most probably you have seen the Dependency Management Plugin.

It comes from the Spring team, but can be applied to any Gradle project:

plugins {
    id "io.spring.dependency-management" version "1.0.6.RELEASE"
}

dependencyManagement {
    dependencies {
        dependency 'org.corp:lib:1.2.3'
    }
}

dependencies {
    compile "org.corp:lib" // Look, Ma, no version at all!
}

And, since the plugin is focused on the dependency management, it comes with some neat features:

dependencyManagement {
    dependencies {
        // Single dependency
        dependency 'org.corp:lib:1.2.3'

        // multiple dependencies under the same group
        dependencySet(group:'org.slf4j', version: '1.7.7') {
            entry 'slf4j-api'
            entry 'slf4j-simple'
        }
    }

    imports {
        // Who does not like BOMs?
        mavenBom 'com.fasterxml.jackson:jackson-bom:2.9.9'
    }
}

I encourage you to try it, especially if you’re using Spring Boot (you transitively have the plugin already).

How to: imperative way

Another way of managing the dependencies in your project in centralized way is to use Gradle’s built-in mechanism:

configurations.all {
    resolutionStrategy.eachDependency {
        switch (requested) {
            case { it.group == "org.slf4j" }:
                return useVersion("1.7.7")
            case { it.group == "org.corp" && it.name == "lib" }:
                return useVersion("1.2.3")
        }
    }
}

dependencies {
    compile "org.corp:lib" // Look, Ma, no version too!
}

Or string-based version:

configurations.all {
    resolutionStrategy.eachDependency {
        switch (requested.module.toString()) {
            case ~/org.slf4j:.*/:
                return useVersion("1.7.7")
            case "org.corp:lib":
                return useVersion("1.2.3")
        }
    }
}

And, while it may not look like the cleanest approach, it is imperative and you can source versions from a file, for instance. Cool, huh?

Who is using this approach? Atlassian, for instance:)

See the docs for more details on how to use it.

Conclusion

Gradle is very powerful and flexible. Even things like the dependency management can be pluggable and/or customizable. I believe we can do better than variables, and, personally, I find the described options more flexible and cleaner due to the fact that you can avoid specifying the version at all.

Also, I didn’t try the dependency locking in Gradle yet because it is a bit hard to manage due to many created files (1 per configuration):

… but it may become a nice way to manage the versions too.

I’m sure there are even more ways of doing it, maybe with Gradle’s other mechanisms (sometimes Gradle is a bit too flexible 😅). What’s your favourite?

comments powered by Disqus