Введение: почему все перешли на build.gradle.kts

В новых проектах файлы сборки заканчиваются на .kts, а в старых — на .gradle. Если на собеседовании спросить, чем они отличаются, многие пожимают плечами: «ну, это Kotlin вместо Groovy». А за этим «ну» скрывается целая история про типизированные аксессоры и IDE-поддержку.

Kotlin DSL — это способ писать скрипты сборки Gradle на Kotlin вместо Groovy. Формально можно сказать «то же самое, только на другом языке», и не ошибиться. Но суть не в языке, а в том, что скрипт на Kotlin — это настоящий код: он компилируется, проверяется типами, и IDE помогает в нём ориентироваться.

Тема не самая хайповая, но на собеседованиях про сборку спрашивают регулярно: чем отличаются DSL, что такое типизированные аксессоры, почему implementation пишется без кавычек. Разберём, как это работает на самом деле.

Groovy DSL vs Kotlin DSL

У Gradle два варианта скриптов сборки, и официальная документация описывает их так:

  • Groovy DSL — скрипты с расширением .gradle.
  • Kotlin DSL — скрипты с расширением .gradle.kts.

Kotlin DSL появился как альтернатива Groovy DSL и даёт улучшенное редактирование в IDE: автодополнение, рефакторинг, документацию. Он полностью поддерживается IntelliJ IDEA и Android Studio. При этом оба варианта построены на одном и том же Java API Gradle — это не две разные системы, а два способа описать одно и то же.

Ключевая мысль из документации: всё в Kotlin DSL-скрипте — это код Kotlin, который компилируется и исполняется Gradle. Не текст, который Gradle «читает», а программа, которую он собирает и запускает на фазе конфигурации.

Как запомнить

Groovy DSL — динамика: имена берутся из строк. Kotlin DSL — статика: всё проверяется типами при компиляции скрипта. Ошибка в build.gradle.kts видна в IDE ещё до запуска сборки.

Можно даже смешивать: Gradle разрешает в одном билде держать и .gradle, и .gradle.kts. Но новые проекты Android Studio создаёт только на Kotlin DSL, и Google выпускает официальный гайд по миграции с Groovy на Kotlin DSL.

Типизированные аксессоры

Главное отличие, которое стоит понимать, — type-safe model accessors (типизированные аксессоры). В Groovy DSL доступ к элементам модели сборки — конфигурациям, задачам, extension'ам — происходит по именам, которые резолвятся динамически, во время выполнения. Ошибка в имени проявится только при запуске сборки.

Kotlin DSL заменяет динамический поиск типизированными аксессорами: элементы, которые добавляют плагины, становятся доступны как типизированные функции и свойства. Это описано в Kotlin DSL primer. Сравните сами:

build.gradle
// Groovy: имена как строки, проверки нет
dependencies {
    implementation 'androidx.appcompat:appcompat:1.8.0'
    testImplementation 'junit:junit:4.13.2'
}
build.gradle.kts
// Kotlin: типизированные аксессоры, IDE знает про implementation
dependencies {
    implementation("androidx.appcompat:appcompat:1.8.0")
    testImplementation("junit:junit:4.13.2")
}

В Groovy implementation — строка-имя. В Kotlin — настоящая функция, которую добавил плагин, и IDE подскажет её с автодополнением. То же с задачами и extension'ами:

build.gradle.kts
plugins {
    id("com.android.application")
}

android {
    namespace = "com.example.myapp"
    compileSdk = 36
}

tasks {
    // ленивая настройка задачи: только если она реально нужна
    test {
        testLogging.showExceptions = true
    }
}

Важный нюанс: типизированные аксессоры формируются сразу после блока plugins {}. Если конфигурацию добавить вручную позже, аксессора для неё не будет — придётся обращаться по имени через named(), configure<T>() или строковые кавычки. Диагностировать такие случаи помогает задача ./gradlew kotlinDslAccessorsReport: она показывает, какие аксессоры доступны.

Тот же приём работает и для плагинов, подключённых старым способом через apply(plugin = "..."): типизированных аксессоров для них не будет, и конфигурации придётся настраивать по имени:

build.gradle.kts
apply(plugin = "java-library")

dependencies {
    "implementation"("junit:junit:4.13.2") // строка вместо аксессора
}

tasks {
    named<Test>("test") {
        testLogging.showExceptions = true
    }
}

Именно поэтому блок plugins {} так важен: он не только рекомендуемый способ подключения, но и источник типизированных аксессоров, которые делают скрипт безопасным и удобным.

Ошибка-классика: кандидат говорит, что Kotlin DSL — это «просто другой синтаксис». Нет: это другой подход к разрешению имён. Типизированные аксессоры — то, что отличает Kotlin DSL от Groovy DSL на уровне архитектуры, и именно это спрашивают на собеседовании.

Файлы проекта на Kotlin DSL

Весь проект на Kotlin DSL — это три файла с расширением .gradle.kts. Настройки проекта:

settings.gradle.kts
pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}
rootProject.name = "My Application"
include(":app")

Корневой файл с версиями плагинов (обратите внимание на apply false — плагин объявляется, но не применяется к корню):

build.gradle.kts
plugins {
    id("com.android.application") version "9.3.0" apply false
    id("org.jetbrains.kotlin.android") version "2.3.21" apply false
}

И модульный файл, который настраивает сам модуль:

app/build.gradle.kts
plugins {
    id("com.android.application")
}

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

    defaultConfig {
        applicationId = "com.example.myapp"
        minSdk = 23
        targetSdk = 36
    }

    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
        }
    }
}

dependencies {
    implementation(project(":lib"))
    implementation("androidx.appcompat:appcompat:1.8.0")
}

Обратите внимание на детали, которые выдают Kotlin: namespace = "..." — присваивание свойства, isMinifyEnabled — префикс is для boolean-свойств, getByName("release") — обращение к задаче по имени из контейнера. В Groovy всё это выглядело бы иначе, и кандидаты, которые «переписали» Groovy-скрипт в Kotlin механически, часто путают именно эти места.

Практика: что важно знать про Kotlin DSL

Несколько фактов, которые показывают глубину понимания:

  • Implicit imports. Скрипты Kotlin DSL получают набор неявных импортов: API Gradle, пакет org.gradle.kotlin.dsl и несколько Java-классов (java.io.File, javax.inject.Inject). Поэтому tasks и dependencies работают без единой строки импорта.
  • Плагины через plugins {}. Использование блока plugins {} для подключения плагинов заметно улучшает редактирование в IDE — потому что именно оттуда берутся типизированные аксессоры.
  • Скрипт компилируется на фазе configuration. Ошибка компиляции build.gradle.kts — это ошибка сборки ещё до выполнения задач. Предупреждения Kotlin-компилятора показываются в консоли при конфигурации.
  • Ленивая конфигурация. В Kotlin DSL принято использовать register и tasks { ... }, чтобы тяжёлые задачи настраивались только при необходимости — это ускоряет конфигурацию крупных проектов.
  • Precompiled script plugins. Общую конфигурацию модулей выносят в свои плагины-скрипты (convention plugins, обычно в каталог buildSrc): тогда «подключение стандарта» в модуле — одна строка в plugins {}, а правки живут в одном месте. Это естественный следующий шаг после переезда на Kotlin DSL.
  • .gradle — Groovy DSL, .gradle.kts — Kotlin DSL; оба работают поверх Java API Gradle.
  • Типизированные аксессоры появляются после блока plugins {} и дают автодополнение в IDE.
  • implementation("...") — функция, а не строка; isMinifyEnabled, getByName — типичные Kotlin-конструкции в скриптах.
  • Kotlin DSL-скрипты компилируются на фазе configuration; ошибки типов видны до запуска сборки.
  • Новые проекты Android Studio — на Kotlin DSL; для старых есть официальный гайд миграции.

На собеседованиях я часто вижу, как кандидаты уверенно отвечают про корутины и архитектуру, но теряются на вопросе про сборку — потому что «это же не программирование». А это программирование: типобезопасность, ленивость, компиляция. Тот, кто это понимает, выделяется сразу — и такие темы отлично закрывают разницу между junior и middle.

Хотите закрыть пробелы между junior и middle?

Kotlin DSL, сборка, архитектура — темы, которые отличают «пишущего код» от «понимающего систему». Бесплатная диагностика: разберу ваши пробелы и составлю план роста за 30–40 минут, без обязательств.

Итоги

Kotlin DSL — не косметическое изменение, а смена подхода к скриптам сборки.

  • Формат: .gradle.kts вместо .gradle; скрипты — настоящий Kotlin-код, который компилируется.
  • Суть: типизированные аксессоры вместо динамических имён — автодополнение и проверка типов в IDE.
  • Практика: новые проекты только на Kotlin DSL, для старых есть официальный гайд миграции.

Главное, что нужно запомнить

Kotlin DSL — это типобезопасность для скриптов сборки: аксессоры из plugins {}, компиляция на фазе configuration, поддержка в Android Studio. Объяснили это — вопрос про сборку закрыт.

Если хотите проверить себя — откройте build.gradle.kts своего проекта и попробуйте объяснить каждую строку. Не получилось — напишите в Telegram, разберём ваш проект и подготовку вместе.