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:
1. **Unused dependencies:** Libraries declared in `dependencies { ... }` that your source code never references. You can delete these immediately.
2. **Transitively used dependencies:** Libraries drawn in by a "big" parent library that your code *does* reference directly.
3. **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'
}
### 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'
}
---
## 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'
}
---
## Summary Checklist
1. Run `./gradlew buildHealth` to get a list of **Unused** and **Transitively Used** dependencies.
2. Delete all **Unused** lines from `dependencies { }`.
3. For **Transitively Used** dependencies, add the specific lower-level artifact directly to `dependencies { }` and remove the top-level parent library.
4. Run `./gradlew test` to ensure your runtime classpath remains intact.
Top comments (1)
I found the step-by-step approach to auditing and culling unnecessary dependencies in Gradle 7.5 to be really helpful, especially the use of the
gradle-dependency-analysis-pluginto scan bytecode against the declared dependency graph. The example of replacing the parent library with a specific modular artifact, such as swappingaws-java-sdkforaws-java-sdk-s3, is a great illustration of how to reduce dependency bloat. Have you considered using Gradle'sdependencyInsightreport in conjunction with the plugin to identify potential issues before running thebuildHealthtask, or are there any specific scenarios where one approach is more suitable than the other?