In a multi-project Gradle build, what does declaring a plugin with `apply false` in the root build script do, and why would you do it?
answer
- resolve but don't apply
- pin version once at root
- subprojects apply by id, no version
- root isn't a code module
- puts plugin on build classpath
basics
~10 sapply false adds the plugin to the build's classpath and resolves its version, but does NOT apply (activate) it to the root project. Subprojects can then apply it by id with no version.
solid answer
~40 sIn the root `plugins {}` block you write `id('...') version '...' apply false`. This tells Gradle to resolve the plugin (download it, put it on the build classpath) but not actually apply its tasks/extensions to the root project. The point is single-source version management: you pin the version once at the root, and each subproject that needs the plugin just writes `id('...')` with no version. This keeps all subprojects on the same plugin version, avoids repeating the version string, and avoids the error you'd get from trying to declare the same plugin version in multiple sibling projects on the same classpath. The root itself usually isn't a code module, so applying the plugin there would be wrong — `apply false` is exactly the 'resolve but don't activate' switch.
code
kotlin · 9 lines// root build.gradle.kts — resolve + pin, do not activate
plugins {
id("org.jetbrains.kotlin.jvm") version "1.9.24" apply false
}
// app/build.gradle.kts — activate, version inherited
plugins {
id("org.jetbrains.kotlin.jvm")
}go deeper
Know the one-liner: apply false resolves/pins the version at the root but doesn't activate the plugin there; subprojects apply by id without a version.
Explain the resolve-vs-apply split and why repeating a version across siblings fails, motivating central declaration.
Tie it to convention-plugin / version-catalog strategies and explain the root as a version registry that shouldn't take on plugin behavior.
Frame it as an org-wide build-governance lever — one pinned version, reproducible builds, and how it interacts with included builds and catalogs across many repos.
## The problem `apply false` solves In a multi-project Gradle build you have a root project and several subprojects. Many subprojects want the same plugin (say the Kotlin JVM plugin), and you want them all on the **same version**. If each subproject writes `id('org.jetbrains.kotlin.jvm') version '1.9.24'`, you repeat the version everywhere and risk drift. The `plugins {}` block has a rule: a given plugin version can only be **requested with a version once** across projects that share a classpath. Declaring the version in two sibling projects fails. So you need one place to pin the version. ## How `apply false` works The `plugins {}` block does two conceptually separate things: 1. **Resolve** the plugin — find the marker/jar, download it, and put its classes on the *build script classpath*. 2. **Apply** the plugin — actually run its `apply(project)` logic, adding tasks, extensions, and conventions to *this* project. Writing `apply false` performs step 1 but skips step 2 *for the current project*. The plugin is now on the classpath and its version is fixed, but nothing is activated here. ```kotlin // root build.gradle.kts plugins { id("org.jetbrains.kotlin.jvm") version "1.9.24" apply false id("com.github.johnferguson.gradle.shadow") version "8.1.1" apply false } ``` Then in a subproject: ```kotlin // app/build.gradle.kts plugins { id("org.jetbrains.kotlin.jvm") // NO version — inherited from root } ``` Because the root already resolved and pinned the version, the subproject can apply it by id alone. Gradle finds it already on the classpath. ## Why apply it at all in the root The root project is usually an aggregation point, not a code module — applying the Kotlin plugin there would create a Kotlin source set you don't want. `apply false` lets the root act as the **version registry** without taking on the plugin's behavior. ## Where this fits in the modern setup `apply false` is the classic pre–version-catalog way to centralize plugin versions. With a version catalog you instead write `alias(libs.plugins.kotlin.jvm) apply false` in the root and `alias(libs.plugins.kotlin.jvm)` in subprojects — the `apply false` mechanic is identical, only the version source moves to `libs.versions.toml`.
- If you remove `apply false` from the root declaration, what happens?The plugin gets applied to the root project too — adding its tasks/source sets/extensions to a project that usually shouldn't have them (e.g. a Kotlin source set on an aggregation-only root).
- Can a subproject override the version inherited from the root?No — if the root requested the version with `apply false`, a subproject must apply it with no version. Re-requesting a version for the same plugin on the shared classpath causes an error. To diverge, that subproject needs its own buildscript-classpath setup.
Like buying one shared software license at the org level (resolving + pinning the version) but only installing the app on the laptops that actually need it (applying in subprojects).
saying these in an interview costs you the question
- Saying `apply false` disables or removes the plugin — it still resolves and stays on the classpath; it just isn't applied here.
- Thinking subprojects must still repeat the version after a root `apply false` declaration.