DEV Community

Query Filter
Query Filter

Posted on

gradle-52

To clean up your build configuration and strip out heavy "fat" dependencies while keeping only what your codebase actually imports, you can use a combination of Gradle's built-in dependency analysis tools and the gradle-dependency-analysis-plugin.

Here is a step-by-step approach to audit, isolate, and cull unnecessary dependencies in Gradle 7.5.

Step 1: Run the Built-In Dependency Insight
Before removing anything, run Gradle’s dependency insight report to identify why a heavy transitive library is being pulled into your build graph and which top-level dependency introduced it.

In your terminal, run:

Bash
./gradlew dependencyInsight --dependency --configuration compileClasspath
This generates a dependency tree showing the exact path from your build.gradle declaration down to the nested artifact.

Step 2: Use the Dependency Analysis Plugin (Automated Audit)
The most accurate way to find unused direct dependencies (and indirect dependencies you should declare explicitly) is Autonomous Apps' dependency-analysis-gradle-plugin. It scans your bytecode against your declared dependency graph.

Add it to your root build.gradle:

Groovy
plugins {
id 'com.autonomousapps.dependency-analysis' version '1.19.0' // Fully compatible with Gradle 7.5
}
Then run the audit task:

Bash
./gradlew buildHealth
What the report tells you:
Unused dependencies: Libraries declared in dependencies { ... } that your source code never references. You can delete these immediately.

Transitively used dependencies: Libraries drawn in by a "big" parent library that your code does reference directly.

Undeclared dependencies: Dependencies you rely on in code but only have access to because another library drew them in.

Step 3: Cull the Big Library & Promote the Transitive Artifact
Once you identify that you only use a small sub-module or underlying transitive dependency from a heavy framework:

Option A: Replace the Parent Library with the Specific Module
Many large frameworks (like AWS SDK, Jackson, Spring, or Google Guava/GCP) split their functionality into smaller artifacts. Instead of pulling in the top-level bundle, replace it with only the required sub-module.

Groovy
dependencies {
// ❌ Heavy bundle
// implementation 'com.amazonaws:aws-java-sdk:1.12.xxx'

// ✅ Specific modular artifact
implementation 'com.amazonaws:aws-java-sdk-s3:1.12.xxx'
Enter fullscreen mode Exit fullscreen mode

}
Option B: Exclude the Heavy Parent and Declare the Sub-Dependency
If a library pulls in heavy transitive dependencies you don't want (like logging frameworks, UI tools, or large utility suites), explicitly exclude them:

Groovy
dependencies {
implementation('com.example:some-library:2.1.0') {
// Exclude the heavy transitive artifact
exclude group: 'org.heavy.framework', module: 'heavy-core'
}

// Explicitly declare ONLY the lightweight sub-dependency your code actually uses
implementation 'org.heavy.framework:lightweight-utils:1.0.0'
Enter fullscreen mode Exit fullscreen mode

}
Step 4: Use Gradle Constraints for Version Control
When you strip out a top-level library and explicitly declare its underlying transitive library, use constraints to manage the version cleanly without locking yourself into rigid hardcodes:

Groovy
dependencies {
constraints {
implementation('net.bytebuddy:byte-buddy:1.14.12') {
because 'Explicitly managed version after removing parent library'
}
}

// Direct lightweight declaration
implementation 'net.bytebuddy:byte-buddy'
Enter fullscreen mode Exit fullscreen mode

}
Summary Checklist
Run ./gradlew buildHealth to get a list of Unused and Transitively Used dependencies.

Delete all Unused lines from dependencies { }.

For Transitively Used dependencies, add the specific lower-level artifact directly to dependencies { } and remove the top-level parent library.

Run ./gradlew test to ensure your runtime classpath remains intact.

Top comments (1)

Collapse
 
topstar_ai profile image
Luis Cruz

I particularly appreciated the step-by-step approach to auditing and optimizing dependencies in Gradle 7.5, especially the use of the gradle-dependency-analysis-plugin to identify unused direct and indirect dependencies. The example of replacing the AWS SDK heavy bundle with the specific aws-java-sdk-s3 submodule is a great illustration of how this process can help reduce the overall size of the dependency graph. Have you found any scenarios where this approach doesn't quite work as expected, such as when dealing with complex transitive dependencies or multi-module projects?