build.gradle: What It Is, Syntax and Configuration in Android

Author: IT Sectr Published: 2026-05-31 Reading time: 9 min

build.gradle is the main build file of an Android project on Gradle that contains instructions for compiling, packaging and signing the application. Each module in the project has its own build.gradle: one at the project level (project-level) and one for each module (module-level). According to Google Android Developers, 2025, proper build.gradle configuration speeds up builds by up to 40% and eliminates dependency conflicts. The syntax supports two languages: Groovy (build.gradle) and Kotlin DSL (build.gradle.kts).

Key Takeaways

  • build.gradle is a Gradle build file with plugin settings, dependencies and Android configuration.
  • Project-level sets plugins and repositories for all modules.
  • Module-level contains the android block with buildTypes, productFlavors and sourceSets.
  • Groovy vs Kotlin DSL — two syntaxes; Kotlin DSL is preferred due to type-safety.
  • dependencies manages libraries: implementation, api, compileOnly, runtimeOnly.

What is build.gradle?

build.gradle is a build script written in Groovy (.gradle extension) or Kotlin (.gradle.kts) that manages all aspects of Android application compilation. Gradle is an automated build system adopted by Google in 2013 as the standard for Android. build.gradle describes: which plugins are applied (Android, Kotlin, libraries), which dependencies are connected, which SDK versions are used, how to sign the application and where to publish.

The build process includes three phases: Initialization (module discovery), Configuration (executing build.gradle scripts), Execution (executing tasks). build.gradle runs during the Configuration phase, when Gradle creates the task graph. At this point Build Variants are determined, dependencies are computed and tasks are configured. Important: build.gradle is code, not just configuration. It can use conditions, loops, method calls and external scripts.

Gradle files are stored in the module root (app/build.gradle) and the project root (build.gradle). Additionally, Gradle supports apply from — including external Gradle scripts. This allows extracting repetitive logic into files with shared settings. With the advent of Convention Plugins (AGP 7+), apply from is considered deprecated — Convention Plugins provide a type-safe and composable way to reuse configuration across modules.

Evolution of build.gradle

Since 2013, build.gradle syntax has undergone significant changes: from Groovy with dynamic configurations to Kotlin DSL with compile-time checks. AGP has evolved from version 1.0 to 8.7 (2025). Key milestones: AGP 3.0 (Java 8 desugar, new variant API), AGP 4.0 (view binding, Java 11), AGP 7.0 (Kotlin DSL by default, Java 11 min), AGP 8.0 (non-transitive R classes, build config in Kotlin), AGP 8.7 (KSP instead of kapt, fast configuration).

Project-level and Module-level build.gradle

Project-level build.gradle (root) defines plugins, repositories and configurations common to all modules. Main blocks: plugins (Gradle plugin declarations), repositories (dependency sources: mavenCentral, google, jitpack). The root build.gradle usually does not have an android block — it appears in modules. Project-level can also contain a subprojects block for common configuration of all subprojects, although Convention Plugins are preferred.

Module-level build.gradle (e.g. app/build.gradle) describes a specific module. If the module is an application, it applies the com.android.application plugin. If a library — com.android.library. Module-level contains: android block (compileSdk, defaultConfig, buildTypes, productFlavors), dependencies block (module dependencies) and optionally blocks for test configuration and packaging. Module-level runs after project-level and can override common settings.

Starting from AGP 8.0, the root build.gradle can use version catalogs (libs.versions.toml) for centralized dependency version management. A version catalog is a file in the gradle/ directory that contains versions, libraries and plugins. In build.gradle dependencies are connected via libs: implementation(libs.retrofit). Version catalogs are mandatory for new projects and recommended for all projects with three or more modules.

kotlin
// settings.gradle.kts — project root
pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

// build.gradle.kts (project-level)
plugins {
    id("com.android.application") version "8.7.0" apply false
    id("org.jetbrains.kotlin.android") version "2.0.21" apply false
}

// app/build.gradle.kts (module-level)
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("com.google.devtools.ksp")
}

android {
    namespace = "com.example.myapp"
    compileSdk = 35

    defaultConfig {
        applicationId = "com.example.myapp"
        minSdk = 26
        targetSdk = 35
        versionCode = 1
        versionName = "1.0.0"
    }
}

Groovy vs Kotlin DSL

Groovy is a dynamic JVM language that was the original Gradle syntax. Groovy scripts (.gradle) use dynamic typing: you can omit types, use quoted or unquoted strings, call methods that don't exist at compile time. Groovy's flexibility is also its drawback: the IDE cannot verify syntax and types until the script is executed, leading to runtime errors with incorrect parameter names or types.

Kotlin DSL (.gradle.kts) uses Kotlin's static typing. The IDE checks types, suggests available parameters through autocomplete and highlights errors at edit time. Kotlin DSL is slower during the Configuration phase (due to compiling .kts files to bytecode), but Google continuously improves performance: AGP 8.5+ uses Gradle Configuration Cache and Caching Kotlin DSL compilation, reducing the difference to 1-2 seconds.

Google recommends Kotlin DSL for all new projects and gradual migration of existing ones. Migration from Groovy to Kotlin DSL is straightforward: quotes are replaced with parentheses, types are added, operators are converted to functions. Most libraries provide Kotlin DSL examples in their documentation. For complex cases (Custom Plugin, Task Graph), Kotlin DSL provides a type-safe API and prevents errors that in Groovy are only discovered at runtime. Version catalogs (libs.versions.toml) work identically with both syntaxes.

FeatureGroovy (.gradle)Kotlin DSL (.gradle.kts)
TypingDynamicStatic
IDE supportLimitedFull (autocomplete, types)
Configuration speedFaster (no compilation)Slower (.kts compilation)
ErrorsRuntimeCompile-time
RecommendationLegacy projects onlyNew projects and migration

The android Block: App Configuration

compileSdk, minSdk and targetSdk

Block android is the central element of module-level build.gradle. Inside it are configured: namespace (for R and BuildConfig), compileSdk, defaultConfig, buildTypes, productFlavors, sourceSets, compileOptions, packaging, bundle. All parameters of the android block apply only to Android modules. If the module is a library, the library plugin is used instead of application, and applicationId is absent from the android block.

compileSdk is the SDK version used to compile the code. It should be the latest Android API (at the time of writing — 35). minSdk is the minimum API version for support. targetSdk is the version the app targets (behavioral changes of this version apply). The difference between compileSdk and targetSdk: compileSdk determines available APIs, targetSdk determines runtime behavior. Recommendation: compileSdk = latest, targetSdk = latest - 1 (for testing adaptation to new changes).

compileOptions sets Java compatibility: sourceCompatibility and targetCompatibility. AGP 8+ requires Java 17+ for compilation. packaging manages file inclusion from libraries: exclude, merge, pickFirst for resolving META-INF conflicts. buildFeatures enables/disables ViewBinding, DataBinding, Compose. aaptOptions configures resource processing: ignoreAssetsPattern, cruncherEnabled. Each element of the android block optimizes a specific aspect of the build.

kotlin
android {
    namespace = "com.example.myapp"
    compileSdk = 35
    buildToolsVersion = "35.0.0"

    defaultConfig {
        applicationId = "com.example.myapp"
        minSdk = 26
        targetSdk = 35
        versionCode = 5
        versionName = "2.3.1"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        getByName("debug") { isDebuggable = true }
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
        }
    }

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }

    buildFeatures {
        viewBinding = true
        compose = true
    }
}

Dependency Management

BOM (Bill of Materials)

Dependencies in build.gradle are libraries and modules connected to the project. The dependencies block sits at the same level as the android block. Gradle supports several configurations: implementation (library is available in this module, not transitive), api (library is transitively available to dependent modules), compileOnly (compile-time only, not included in APK), runtimeOnly (runtime only), annotationProcessor / ksp (annotation processors), testImplementation (test only), androidTestImplementation (instrumentation tests only).

Starting from AGP 8.0, Non-Transitive R classes — each library has its own R class, preventing resource conflicts. In the dependencies block it is important to use the correct configurations: implementation does not expose transitive dependencies, speeding up builds. api exposes them — used when a library exports types from another library (e.g. Retrofit uses OkHttp types in its public API).

For version management it is recommended to use BOM (Bill of Materials) — a build file that defines compatible library versions. Firebase BOM: implementation(platform("com.google.firebase:firebase-bom:33.0.0")). After connecting BOM, you can specify only the library name without version — BOM will automatically select a compatible version. This eliminates conflicts between transitive dependencies of different libraries. BOMs are available for Firebase, Compose, Kotlin, Ktor, AndroidX.

kotlin
dependencies {
    // BOM — version management
    implementation(platform("androidx.compose:compose-bom:2024.12.01"))
    implementation(platform("com.google.firebase:firebase-bom:33.0.0"))

    // AndroidX and Compose
    implementation("androidx.core:core-ktx")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx")
    implementation("androidx.activity:activity-compose")
    implementation("androidx.compose.ui:ui")

    // Network
    implementation("com.squareup.retrofit2:retrofit:2.11.0")
    implementation("com.squareup.okhttp3:okhttp:4.12.0")

    // Firebase (versions from BOM)
    implementation("com.google.firebase:firebase-firestore")
    implementation("com.google.firebase:firebase-crashlytics")

    // Testing
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.2.1")
}

build.gradle in Multi-Module Projects

In multi-module projects, each module has its own build.gradle. To connect one module to another, use the syntax implementation(project(":module-name")). Gradle automatically rebuilds the module if its configuration has changed. Multi-module architecture improves build time (incremental builds, parallelism) and separates responsibilities between feature modules, core modules and libraries.

The key problem of multi-module projects is configuration duplication. If 10 modules have the same minSdk, compileSdk and Compose dependencies, that is 10 copies in different build.gradle files. The solution is Convention Plugins (previously buildSrc). A Convention Plugin is a Gradle plugin written in Kotlin that is applied to modules: plugins { id("myapp.android.library") }. The plugin contains common configuration, and changes apply immediately to all modules.

To organize Convention Plugins, the build-logic/ directory is used in the project root. It contains includeBuild in settings.gradle and Kotlin plugins. Convention Plugins can be published to a maven repository for reuse across projects. Google recommends Convention Plugins as the standard for multi-module projects, replacing subprojects { } and apply from. Switching to Convention Plugins reduces the module's build.gradle to 10-15 lines.

kotlin
// build-logic/src/main/kotlin/AndroidLibraryConventionPlugin.kt
class AndroidLibraryConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        with(target) {
            with(plugins) {
                apply("com.android.library")
                apply("org.jetbrains.kotlin.android")
            }
            extensions.configure<CommonExtension<*, *, *, *>> {
                compileSdk = 35
                defaultConfig { minSdk = 26 }
                compileOptions {
                    sourceCompatibility = JavaVersion.VERSION_17
                    targetCompatibility = JavaVersion.VERSION_17
                }
            }
        }
    }
}

// module/build.gradle.kts — after Convention Plugin
plugins {
    id("myapp.android.library")
}

dependencies {
    implementation(project(":core:network"))
}

Frequently Asked Questions

Which language should I choose for build.gradle in 2025?

Kotlin DSL (.gradle.kts) is Google's official recommendation. Static typing prevents errors, the IDE provides autocomplete. Groovy (.gradle) is supported, but new Gradle and AGP features are tested primarily on Kotlin DSL.

Why is namespace needed in build.gradle?

namespace defines the package for generated classes (R.java, BuildConfig). Previously namespace was set in AndroidManifest.xml. Starting with AGP 7+, namespace is specified only in build.gradle. The value must match applicationId (or differ if applicationIdSuffix is used).

How to speed up Gradle builds?

Enable Gradle Configuration Cache (org.gradle.configuration-cache=true), use Build Cache (org.gradle.caching=true), switch to KSP instead of kapt, split the multi-module project and use Convention Plugins. Also disable unnecessary product flavors: for debug builds only one flavor.

What is the difference between implementation and api?

implementation: the dependency is visible only inside the module. Dependent modules do not get access to transitive classes. api: the dependency is exposed externally. Use api when types from the dependency are used in the module's public API (for example, Retrofit exports OkHttp types). implementation speeds up builds — Gradle does not rebuild dependent modules when an implementation dependency changes.

Can build.gradle be used for iOS?

build.gradle is an Android-specific file. iOS uses Xcode project (.xcodeproj) and Swift Package Manager (Package.swift). However, there are cross-platform tools (Kotlin Multiplatform, Flutter, React Native) where build.gradle is used to build the Android part. In KMP, build.gradle configures the Android target.

Summary

  • build.gradle is the central build file of an Android project, managing plugins, dependencies and configuration.
  • Project-level defines common plugins and repositories; module-level contains the android block and module dependencies.
  • Kotlin DSL is the recommended syntax for new projects thanks to static typing.
  • The android block configures compileSdk, defaultConfig, buildTypes, productFlavors and sourceSets.
  • Dependencies use implementation (hidden) and api (public); BOM manages versions transitively.
  • Multi-module projects use Convention Plugins to eliminate configuration duplication.
  • Recommendation: migrate to Kotlin DSL, Version Catalogs and Convention Plugins for cleaner and faster builds.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also