Explain the roles of the plugins {} and dependencies {} blocks in build.gradle.kts. How do you apply a plugin and declare an implementation dependency?
answer
- plugins = capabilities/tasks; dependencies = libraries
- implementation hides transitively; api exposes
- compileOnly vs runtimeOnly asymmetry
- plugins {} must come first; it generates accessors
- coordinate = group:artifact:version in double quotes
basics
~10 splugins {} applies Gradle plugins that add build capabilities and tasks. dependencies {} lists the libraries your code needs, grouped by configuration like implementation or testImplementation. You add a library with implementation("group:name:version").
solid answer
~40 sThe plugins {} block declares which Gradle plugins to apply, e.g. kotlin("jvm") version "2.0.0" or id("org.springframework.boot") version "3.x". Applying a plugin registers tasks, conventions, and — crucially in the Kotlin DSL — generates type-safe accessors (like the implementation configuration) that become available in later blocks. The dependencies {} block declares your project's libraries against a configuration: implementation(...) for normal compile+runtime deps that don't leak to consumers, api(...) to expose them transitively, testImplementation(...) for test-only deps, and runtimeOnly(...)/compileOnly(...) for the asymmetric cases. A dependency is a coordinate string "group:artifact:version", or kotlin("test") / project(":other-module") helpers. Because implementation(...) only exists after the relevant plugin (java/kotlin) is applied, ordering matters: plugins {} must precede dependencies {}.
go deeper
Knows plugins apply build features and dependencies list libraries, and can add an implementation dependency correctly.
Distinguishes implementation/api/compileOnly/runtimeOnly and explains plugin-before-dependency ordering.
Explains why implementation improves incremental build performance and how api affects the consumer's compile classpath.
Reasons about dependency hygiene and api/implementation leakage across a multi-module graph and its effect on build times.
## Two distinct jobs `plugins { }` and `dependencies { }` answer two different questions: - **`plugins { }` — "What can my build *do*?"** It applies *Gradle plugins*: reusable bundles of build logic that add tasks (`compileKotlin`, `test`, `bootJar`…), conventions, and DSL extensions. - **`dependencies { }` — "What libraries does my *code* need?"** It declares the external/internal libraries to put on the various classpaths. ## Applying plugins ```kotlin plugins { kotlin("jvm") version "2.0.0" // kotlin("x") == id("org.jetbrains.kotlin.x") id("org.springframework.boot") version "3.3.0" `java-library` // core plugin, no version, backtick-escaped id } ``` - `id("...")` references a plugin by id; `version "..."` pins it. - `kotlin("jvm")` is a Kotlin-DSL helper that expands to the JetBrains plugin id. - **Core plugins** (e.g. `` `java-library` ``, `application`) need no version. Ids containing hyphens are wrapped in backticks because of Kotlin identifier rules. Applying a plugin is what makes **type-safe accessors** appear: the Java/Kotlin plugin creates the `implementation`, `api`, `testImplementation` configurations and the DSL surfaces them as typed functions. ## Declaring dependencies ```kotlin dependencies { implementation("com.squareup.okhttp3:okhttp:4.12.0") // "group:artifact:version" api("org.slf4j:slf4j-api:2.0.13") // exposed to consumers compileOnly("org.projectlombok:lombok:1.18.32") // compile, not runtime runtimeOnly("com.h2database:h2:2.2.224") // runtime, not compile testImplementation(kotlin("test")) // helper for kotlin-test implementation(project(":shared")) // another module } ``` ## Configurations cheat-sheet - **`implementation`** — on compile + runtime classpath, **not** exposed transitively. The default choice; faster builds because changing it doesn't recompile consumers. - **`api`** — like implementation but **leaks** to consumers (only from the `java-library` plugin). Use when your public API exposes the dependency's types. - **`compileOnly`** — present at compile time only (annotation processors' APIs, provided libs). - **`runtimeOnly`** — present at runtime only (JDBC drivers, logging backends). - **`testImplementation` / `testRuntimeOnly`** — same idea, scoped to test source set. ## Ordering matters Because `implementation(...)` is generated by applying the Java/Kotlin plugin, the `plugins { }` block must come **before** `dependencies { }`. Put `plugins { }` first in the file (Gradle even requires `plugins { }` to be one of the very first statements). ## Common mistake Writing `implementation 'group:name:version'` (Groovy style) fails in `.kts`: you need parentheses and double quotes — `implementation("group:name:version")`.
- When should you use api instead of implementation?Only when your module's public API returns or accepts types from that dependency, so consumers need it transitively. Otherwise implementation keeps the dependency internal and speeds up recompilation.
- Why does plugins {} need to appear near the top of the file?Gradle parses it early to resolve and apply plugins before the rest of the script, and the type-safe accessors (like implementation) only exist once the plugin is applied.
saying these in an interview costs you the question
- Confusing dependencies (libraries) with plugins (build logic)
- Using implementation for everything when api is required, or vice versa, with no rationale
- Putting dependencies {} before plugins {} and expecting accessors to resolve
- Groovy-style unparenthesized single-quoted coordinates in .kts
- Thinking compileOnly deps are available at runtime