Backend Development 17 min read

Understanding Gradle Dependencies Graph Symbols

The article explains Gradle's dependency‑graph symbols—arrow (→) for version conflicts, (c) for constraint‑driven versions, (*) for omitted subtrees, and markers for strict or forced versions—showing how to read the output, identify conflicts, and apply proper resolution strategies.

NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
Understanding Gradle Dependencies Graph Symbols

Gradle's dependencies command is frequently used to locate version‑related issues in third‑party libraries. This article analyses the special symbols that appear in the command output and explains how they affect dependency resolution.

The most common symbols are:

-> – indicates a version conflict where the selected version is upgraded to a higher one.

(c) – marks a dependency that comes from a constraints block.

(*) – denotes an omitted subtree that has already been displayed elsewhere.

Dependency resolution

The -> arrow shows that the original version (left side) was replaced by the version on the right because of a conflict. Gradle generally selects the highest version, though there are many edge cases.

// module A, tag 1.0.0, build.gradle
dependencies {
    implementation 'com.netease.cloudmusic.android:module_c:1.0.0'
    implementation 'com.netease.cloudmusic.android:module_d:1.0.0'
}

// app, build.gradle
dependencies {
    implementation 'com.netease.cloudmusic.android:module_a:1.0.0'
}

When a conflict occurs, the output may look like:

+--- com.netease.cloudmusic.android:module_a:1.0.0
|    +--- com.netease.cloudmusic.android:module_c:1.0.0 -> 1.1.0
|    \--- com.netease.cloudmusic.android:module_d:1.0.0
\--- com.netease.cloudmusic.android:module_b:1.0.0
    \--- com.netease.cloudmusic.android:module_c:1.1.0

Dependency omitted

The (*) symbol indicates that a subtree has been omitted because it was already printed earlier. This keeps the graph readable for large projects.

+--- com.netease.cloudmusic.android:module_a:1.0.0
|    +--- com.netease.cloudmusic.android:module_c:1.0.0
|    \--- com.netease.cloudmusic.android:module_d:1.0.0
+--- com.netease.cloudmusic.android:module_b:1.0.0
|    \--- com.netease.cloudmusic.android:module_a:1.0.0 (*)
+--- com.netease.cloudmusic.android:module_e:1.0.0
|    \--- com.netease.cloudmusic.android:module_a:1.0.0 (*)
\--- com.netease.cloudmusic.android:module_f:1.0.0
    \--- com.netease.cloudmusic.android:module_a:1.0.0 (*)

Dependency constraint

The (c) marker appears when a version is forced by a constraints block. This allows you to upgrade or align transitive dependencies without adding a direct dependency.

dependencies {
    constraints {
        implementation('com.netease.cloudmusic.android:module_c:1.1.0') {
            because 'previous versions have a bug impacting this application'
        }
    }
}

When the constraint is active, the output shows the (c) marker:

+--- com.netease.cloudmusic.android:module_a:1.0.0
|    +--- com.netease.cloudmusic.android:module_c:1.0.0 -> 1.1.0
|    \--- com.netease.cloudmusic.android:module_d:1.0.0
\--- com.netease.cloudmusic.android:module_c:1.1.0 (c)

If the consuming module later upgrades, the constrained version may become redundant and disappear from the graph.

Downgrading versions

Gradle also supports explicit downgrades via strictly or force . The strictly keyword is displayed in the graph, while force is deprecated in newer Gradle versions.

// app, build.gradle
dependencies {
    implementation 'com.netease.cloudmusic.android:module_a:1.1.0'
    implementation('com.netease.cloudmusic.android:module_c') {
        version { strictly '1.0.0' }
    }
}
+--- com.netease.cloudmusic.android:module_a:1.1.0
|    +--- com.netease.cloudmusic.android:module_c:1.1.0 -> 1.0.0
|    \--- com.netease.cloudmusic.android:module_d:1.1.0
\--- com.netease.cloudmusic.android:module_c:{strictly 1.0.0} -> 1.0.0

Using force produces a similar result but without a visible marker:

// app, build.gradle
dependencies {
    implementation 'com.netease.cloudmusic.android:module_a:1.1.0'
    implementation('com.netease.cloudmusic.android:module_c:1.0.0') { force = true }
}
+--- com.netease.cloudmusic.android:module_a:1.1.0
|    +--- com.netease.cloudmusic.android:module_c:1.1.0 -> 1.0.0
|    \--- com.netease.cloudmusic.android:module_d:1.1.0
\--- com.netease.cloudmusic.android:module_c:1.0.0

Summary

By recognizing the symbols -> , (c) , (*) , and the keywords strictly and force , developers can more accurately interpret Gradle's dependency graph, locate version conflicts, and apply appropriate resolution strategies.

JavaAndroiddependency managementGradleBuild ToolsVersion Conflict
NetEase Cloud Music Tech Team
Written by

NetEase Cloud Music Tech Team

Official account of NetEase Cloud Music Tech Team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.