gradle-expert

Installation
SKILL.md

gradle-expert

Keyword: gradle | Platforms: gemini,claude,codex

Modern Gradle Build Tool Expert Skill - Specialized in performance, dependency management, and polyglot builds.

Core Mandates

  • DSL Proficiency: Expert in both Groovy DSL (build.gradle) and Kotlin DSL (build.gradle.kts).
  • Dependency Configurations: Distinguish between api, implementation, runtimeOnly, and testImplementation.
  • Build Performance: Leverage Build Cache, Daemon, and Parallel execution to optimize feedback loops.
  • Convention over Configuration: Prefer built-in plugins (java-library, application, maven-publish) over custom logic.

build.gradle.kts Examples

Quarkus Project

// build.gradle.kts
plugins {
    java
    id("io.quarkus") version "3.20.1"
}

repositories {
    mavenCentral()
}

val quarkusPlatformVersion: String by project  // from gradle.properties

dependencies {
    // BOM - manages all Quarkus versions
    implementation(enforcedPlatform("io.quarkus.platform:quarkus-bom:${quarkusPlatformVersion}"))

    // No versions needed - managed by BOM
    implementation("io.quarkus:quarkus-rest")
    implementation("io.quarkus:quarkus-arc")
    implementation("io.quarkus:quarkus-hibernate-orm-panache")

    // Test
    testImplementation("io.quarkus:quarkus-junit5")
    testImplementation("io.rest-assured:rest-assured")
}

java {
    sourceCompatibility = JavaVersion.VERSION_21
    targetCompatibility = JavaVersion.VERSION_21
}

tasks.withType<Test> {
    useJUnitPlatform()
    systemProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager")
}

Multi-Module with Version Catalog

# gradle/libs.versions.toml
[versions]
quarkus = "3.20.1"
jackson = "2.18.2"
junit = "5.11.4"
assertj = "3.27.3"

[libraries]
quarkus-bom = { module = "io.quarkus.platform:quarkus-bom", version.ref = "quarkus" }
quarkus-rest = { module = "io.quarkus:quarkus-rest" }
quarkus-arc = { module = "io.quarkus:quarkus-arc" }
quarkus-hibernate = { module = "io.quarkus:quarkus-hibernate-orm-panache" }
quarkus-junit5 = { module = "io.quarkus:quarkus-junit5" }
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
junit-bom = { module = "org.junit:junit-bom", version.ref = "junit" }
assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" }

[plugins]
quarkus = { id = "io.quarkus", version.ref = "quarkus" }
// settings.gradle.kts
rootProject.name = "my-platform"
include("common", "service-user", "service-order")
// build.gradle.kts (root)
plugins {
    java
    alias(libs.plugins.quarkus) apply false
}

subprojects {
    apply(plugin = "java")

    repositories {
        mavenCentral()
    }

    dependencies {
        implementation(platform(libs.quarkus.bom))
        implementation(platform(libs.junit.bom))
    }

    java {
        toolchain {
            languageVersion.set(JavaLanguageVersion.of(21))
        }
    }
}
// service-user/build.gradle.kts
plugins {
    alias(libs.plugins.quarkus)
}

dependencies {
    implementation(project(":common"))
    implementation(libs.quarkus.rest)
    implementation(libs.quarkus.arc)
    implementation(libs.quarkus.hibernate)

    testImplementation(libs.quarkus.junit5)
    testImplementation(libs.assertj)
}

Dependency Configuration Decision Tree

Does the consuming module need this at compile time?
  YES → Does the consuming module also see this in ITS public API?
    YES → api (leaks to consumers' compile classpath)
    NO  → implementation (hidden from consumers)
  NO → Is it needed at runtime only?
    YES → runtimeOnly (e.g., JDBC drivers, SLF4J bindings)
    NO  → Is it only for tests?
      YES → testImplementation
      NO  → compileOnly (e.g., annotation processors, provided-like)

Do vs Don't

// BAD - using 'api' everywhere leaks dependencies, slows compilation
dependencies {
    api("com.google.guava:guava:33.4.0-jre")         // Leaked!
    api("io.quarkus:quarkus-hibernate-orm-panache")    // Leaked!
}

// GOOD - only 'api' what consumers actually need in their code
dependencies {
    api("com.example:shared-dto:1.0")                 // Consumers use these types
    implementation("com.google.guava:guava:33.4.0-jre")  // Internal only
    implementation("io.quarkus:quarkus-hibernate-orm-panache")
}

Version Conflict Resolution Workflow

Step 1: Identify the conflict

# Show dependency tree for a specific configuration
./gradlew dependencies --configuration runtimeClasspath

# Filter for a specific module
./gradlew dependencyInsight --dependency jackson-databind --configuration runtimeClasspath

# Output:
# com.fasterxml.jackson.core:jackson-databind:2.18.2
#    variant "compile" [
#       Requested: 2.14.0  ← conflict
#       Selected:  2.18.2  ← Gradle picked highest
#    ]

Step 2: Understand Gradle's resolution strategy

Gradle uses highest version wins by default. This is usually safe but can break binary compatibility.

Step 3: Resolve

// Option A: Force a specific version (use sparingly)
configurations.all {
    resolutionStrategy {
        force("com.fasterxml.jackson.core:jackson-databind:2.18.2")
    }
}

// Option B: Use a BOM/platform (preferred)
dependencies {
    implementation(platform("com.fasterxml.jackson:jackson-bom:2.18.2"))
    // All Jackson modules now use 2.18.2
}

// Option C: Exclude transitive dependency
dependencies {
    implementation("some-lib:some-lib:1.0") {
        exclude(group = "com.fasterxml.jackson.core", module = "jackson-databind")
    }
}

// Option D: Fail on conflict (strict mode for CI)
configurations.all {
    resolutionStrategy {
        failOnVersionConflict()
    }
}

Resolution Priority

Priority Strategy When to use
1st BOM / platform() When a BOM exists (jackson-bom, netty-bom, quarkus-bom)
2nd Version Catalog Centralize versions in libs.versions.toml
3rd exclude() When one lib brings a known bad transitive
4th force() Last resort - overrides everything, can mask issues

Common Errors & Fixes

"Could not resolve all dependencies"

// Cause: Missing repository or wrong coordinates
repositories {
    mavenCentral()
    // Add private repo if needed:
    maven {
        url = uri("https://nexus.example.com/repository/maven-releases/")
        credentials {
            username = providers.environmentVariable("MAVEN_USER").orNull
            password = providers.environmentVariable("MAVEN_TOKEN").orNull
        }
    }
}

"Execution failed: A valid toolchain could not be found"

// Fix: Specify Java toolchain
java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(21))
    }
}

// Or in gradle.properties:
// org.gradle.java.home=/path/to/jdk21

Build cache not working

# gradle.properties
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.daemon=true
org.gradle.jvmargs=-Xmx4g -XX:+HeapDumpOnOutOfMemoryError
// settings.gradle.kts - remote cache
buildCache {
    local {
        isEnabled = true
    }
    remote<HttpBuildCache> {
        url = uri("https://cache.example.com/cache/")
        isPush = System.getenv("CI") != null  // Only push from CI
    }
}

"Cannot change dependencies after configuration resolution"

// BAD - resolving a configuration during configuration phase
val runtimeJars = configurations.runtimeClasspath.get().files  // Triggers resolution!

// GOOD - defer to execution phase
tasks.register("listDeps") {
    val runtimeCp = configurations.runtimeClasspath
    doLast {
        runtimeCp.get().files.forEach { println(it) }
    }
}

Slow builds diagnostic

# Generate build scan (interactive report)
./gradlew build --scan

# Profile locally
./gradlew build --profile
# Opens report in build/reports/profile/

# Check configuration cache compatibility
./gradlew build --configuration-cache

Slow builds optimization checklist

Check Action
Daemon enabled? org.gradle.daemon=true in gradle.properties
Parallel builds? org.gradle.parallel=true
Build cache? org.gradle.caching=true
Configuration cache? --configuration-cache (Gradle 8.1+)
Unnecessary api deps? Switch to implementation to reduce recompilation
allprojects{} / subprojects{} blocks? Migrate to convention plugins
Task Configuration Avoidance? Use tasks.register not tasks.create

Gradle-to-Bazel Migration

Configuration Mapping Table

Gradle Bazel Notes
api exports = [...] Visible to transitive consumers
implementation deps = [...] Hidden from consumers
runtimeOnly runtime_deps = [...] Not on compile classpath
compileOnly deps = [...] + neverlink = True Compile but not package
testImplementation test target deps Separate test target
project(":sub") "//sub:target" Bazel label
./gradlew build bazel build //... Build all
./gradlew test bazel test //... Test all
Version Catalog rules_jvm_external Different mechanism

Exporting Dependencies for Bazel

# Generate dependency list from Gradle
./gradlew dependencies --configuration runtimeClasspath \
  | grep '\\---' | sed 's/.*--- //' | sort -u

# Then translate to rules_jvm_external format in MODULE.bazel

Advanced Patterns

  • Build Scan: Using Gradle Build Scans for debugging performance and dependency issues.
  • Custom Tasks: Writing idiomatic tasks using the Task Configuration Avoidance API.
  • Version Catalogs: Managing versions and libraries centrally via libs.versions.toml.

Expert Tips

  • Avoid using the deprecated compile configuration; use implementation or api.
  • Use ./gradlew dependencies --configuration runtimeClasspath to visualize the dependency graph.
  • Prefer Kotlin DSL for better IDE support and type safety in complex builds.
  • Use gradle wrapper --gradle-version=8.12 to pin Gradle version for reproducibility.
  • Always use tasks.register (lazy) over tasks.create (eager) for Task Configuration Avoidance.
  • Use ./gradlew build --dry-run to preview task execution order without running anything.

🌐 Gradle Knowledge Sources

Directive: Use web_fetch to find Kotlin DSL syntax, version catalog patterns, or Gradle-to-Bazel migration strategies if a build configuration is complex.

References

Skill Interoperability

The gradle-expert 🐘 skill provides expertise in modern build DSLs and performance, supporting:

  • rules-quarkus 🔧: Facilitates the migration of Gradle-based projects to Bazel.
Related skills
Installs
15
GitHub Stars
1
First Seen
Mar 15, 2026