Dependency management in multi-module gradle projects

Google's approach in this google samples repo seems to be tackling it very well. They actually do it across multiple projects but it applies for multiple modules as well. If you just have one module, don't bother doing this right now, but remember to do it when you decide to extract a module out.

Our main objectives are to

  • Minimise the effort required to update dependecies
  • Maintain consistency in dependency versions across modules

What to do?

Step 1

Declare your dependencies and versions at the root of the project.

$PROJECT_ROOT/dependencies.gradle

ext.versions = [:]

def versions = [:]

versions.support_lib = '26.0.2'
versions.gson = '2.8.0'

ext.versions = versions

ext.deps = [:]
def deps = [:]

def support_lib = [:]
support_lib.app_compat = "com.android.support:appcompat-v7:$versions.support_lib"
support_lib.recycler_view = "com.android.support:recyclerview-v7:$versions.support_lib"
deps.support_lib = support_lib

deps.gson = "com.google.code.gson:gson:$versions.gson"

ext.deps = deps

Attaching versions and deps to extra user properties ext will allow you to access those variables all across the project

Step 2

Import the definitions at the top of root project's buildScript block

$PROJECT_ROOT/build.gradle

buildScript {
apply from: 'dependencies.gradle'

//left out for brevity...
}

Final step

Declare your module dependencies using the variables defined

$PROJECT_ROOT/my-module/build.gradle

//left for brevity...
dependecies {
implementation deps.support_lib.app_compat
implementation deps.support_lib.recycler_view
implementation deps.gson
}

Approach

All we have done is remove duplication by reusing the version and dependency declarations done at one single place. Since all modules point to the same declaration, they all are in sync. And since there is only one declaration per dependency in the project, it is easy to find and update that one dependency.

IntelliJ doesn't help you much here

Make your variable names easier to debug

Once you declare the variables, IntelliJ doesn't auto complete or suggest them else where when you are editing the gradle files. So make sure your variable name usages are proper and easily debuggable. That is intension behind using camel case here(YMMV).

Do not change all the dependencies at once

The error messages are not specific enough to figure out which variable name of dependency path you messed up. You can go ahead and define all the variables at once but add/use them one by one and sync your gradle file after each step to avoid frustrating debugging sessions.

Feedback

What do you think of this approach? Is there a better way? I am actually curious how this changes when we use kotlin to write gradle files. Let me know your feedback.