Gradle From Zero to Hero (Practical Guide + Challenges)
A blog-style Gradle tutorial that takes you from zero to hero: install with the wrapper, recognize key files, write your first build script, create tasks, use plugins and dependencies, and master multi-module and version catalogs. All examples use the Kotlin DSL (build.gradle.kts). Challenges at the end evaluate your knowledge.
What is Gradle (in one sentence): Gradle is a build automation tool that compiles code, runs tests, packages apps, manages dependencies, and can automate anything via tasks.
The 3 key ideas: (1) Projects โ your build is one project or many (multi-module). (2) Tasks โ Gradle runs tasks (compile, test, build, etc). (3) Plugins + Dependencies โ plugins add capabilities; dependencies bring libraries.
1) Install the right way: Gradle Wrapper
Never rely on "whatever Gradle you have installed". Use the wrapper so the whole team uses the same Gradle version.
Commands: ./gradlew --version, ./gradlew tasks, ./gradlew build. Wrapper files: gradlew, gradlew.bat, gradle/wrapper/gradle-wrapper.properties.
2) Files you must recognize
Kotlin DSL (recommended): settings.gradle.kts, build.gradle.kts, gradle.properties.
Groovy DSL (older but common): settings.gradle, build.gradle.
3) Your first build script (Java example)
Kotlin DSL: build.gradle.kts. Run ./gradlew test and ./gradlew build.
plugins {java}repositories {mavenCentral()}dependencies {testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")}tasks.test {useJUnitPlatform()}
4) Tasks: the core of Gradle
List tasks: ./gradlew tasks. Run a task: ./gradlew test. Create your own task (Kotlin DSL):
tasks.register("hello") {group = "demo"description = "Prints hello"doLast {println("Hello from Gradle")}}
Run: ./gradlew hello. Task dependencies (run A before B):
tasks.register("taskA") {doLast { println("A") }}tasks.register("taskB") {dependsOn("taskA")doLast { println("B") }}
5) Plugins: add "powers"
Plugins define conventions and tasks. Spring Boot + Kotlin example (real world) โ build.gradle.kts:
plugins {id("org.springframework.boot") version "3.3.2"id("io.spring.dependency-management") version "1.1.6"kotlin("jvm") version "1.9.24"kotlin("plugin.spring") version "1.9.24"}group = "com.example"version = "0.0.1"java {toolchain {languageVersion.set(JavaLanguageVersion.of(21))}}repositories {mavenCentral()}dependencies {implementation("org.springframework.boot:spring-boot-starter-web")implementation("com.fasterxml.jackson.module:jackson-module-kotlin")implementation(kotlin("reflect"))testImplementation("org.springframework.boot:spring-boot-starter-test")}tasks.test {useJUnitPlatform()}
Run: ./gradlew bootRun, ./gradlew test, ./gradlew bootJar.
6) Dependencies: what you actually install
Common scopes: implementation (compile + runtime, not leaked to consumers), api (leaked to consumers, for libraries), runtimeOnly (runtime only), testImplementation (tests).
See dependency tree: ./gradlew dependencies, ./gradlew dependencyInsight --dependency jackson --configuration runtimeClasspath.
7) Configurations: the "buckets" dependencies go into
Configs are named containers: compileClasspath, runtimeClasspath, testRuntimeClasspath. Example: exclude a transitive dependency:
dependencies {implementation("some:lib:1.0") {exclude(group = "commons-logging", module = "commons-logging")}}
8) settings.gradle: single vs multi-module
Single project โ settings.gradle.kts:
rootProject.name = "my-app"
Multi-module:
rootProject.name = "my-platform"include("api", "service", "common")
Each module has its own build.gradle.kts. Run ./gradlew build for all; ./gradlew :service:test for one module.
9) Version catalogs (clean dependency versions)
Create gradle/libs.versions.toml:
[versions]
springBoot = "3.3.2"
junit = "5.10.2"
[libraries]
junit = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }Use in build.gradle.kts:
dependencies {testImplementation(libs.junit)}
This prevents "version soup" across modules.
10) Performance and build hygiene (senior moves)
Use the Gradle daemon (usually on by default). gradle.properties:
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.caching=trueClean build: ./gradlew clean build. Build scan (if enabled): ./gradlew build --scan.
11) Debugging when builds fail
Run with more info: ./gradlew build --info, ./gradlew build --stacktrace. Common causes: wrong Java version (fix via toolchain), missing repository (mavenCentral() not set), plugin version mismatch, dependency conflict (use dependencyInsight).
Challenges (Test Yourself)
Level 1 โ Basics
- What's the purpose of the Gradle Wrapper and which command uses it?
- What's the difference between
build.gradle.ktsandbuild.gradle? - How do you list all tasks and run only the test task?
- What does
repositories { mavenCentral() }do?
Level 2 โ Real build skills
- Explain
implementationvsapivsruntimeOnly. - How do you see why a specific dependency version was chosen?
- Create a task
printVersionthat prints the project version. - Make task B depend on task A. What keyword do you use?
Hint for #7: Use tasks.register("printVersion") and doLast { println(version) }.
tasks.register("printVersion") {doLast {println(version)}}
Level 3 โ Multi-module
- You have modules
apiandservice. How do you run tests only forservice? - Where do you define included modules? (settings.gradle(.kts) question)
- How do you share common dependencies across modules cleanly?
Level 4 โ Senior-level Gradle
- A build is slow. Name 4 knobs or strategies to speed it up.
- You have a dependency conflict. What 2 Gradle commands do you use first?
- When would you choose Kotlin DSL over Groovy DSL (real reason)?