Why are root-level allprojects {} and subprojects {} blocks discouraged under Project Isolation, and what replaces them?
answer
- Root mutates siblings = violation
- allprojects/subprojects forbidden
- Convention plugin in buildSrc
- Each project applies it via plugins { id(...) }
- Independent, parallel-configurable projects
basics
~10 sThose blocks configure other projects from the root, which is exactly the cross-project mutation Project Isolation forbids. Replace them with convention plugins in buildSrc that each project applies itself.
solid answer
~40 s`allprojects {}` and `subprojects {}` live in the **root** build script and reach down to configure every other project's mutable model. Project Isolation forbids one project from mutating another at configuration time, so these blocks are direct violations — they're the most common reason a build fails isolation. The replacement is a **convention (precompiled script) plugin**: put the shared logic in `buildSrc/src/main/kotlin/my.conventions.gradle.kts` (or an included build), then have each project `apply` it via `plugins { id("my.conventions") }`. Now each project configures **itself** with the shared behavior — no cross-project mutation — which keeps projects independent and lets Gradle configure them in parallel and incrementally. As a bonus, convention plugins are testable, reusable across builds, and don't rely on project evaluation order.
code
kotlin · 7 lines// buildSrc/src/main/kotlin/my.java-conventions.gradle.kts
plugins { `java-library` }
java { toolchain { languageVersion = JavaLanguageVersion.of(21) } }
tasks.withType<Test>().configureEach { useJUnitPlatform() }
// each subproject's build.gradle.kts
plugins { id("my.java-conventions") }go deeper
Know that root allprojects/subprojects mutate other projects and that convention plugins applied per-project replace them.
Explain why the mutation breaks isolation and how to structure conventions in buildSrc.
Discuss migration: splitting conventions, included builds, and avoiding reintroducing root mutation.
Define the org convention-plugin standard so large builds stay isolation-compliant by default.
## What these blocks do In many older Gradle builds the root `build.gradle(.kts)` contains: ```kotlin subprojects { apply(plugin = "java-library") tasks.withType<Test>().configureEach { useJUnitPlatform() } } ``` This runs from the **root project** and **mutates every subproject's model**. It's convenient but it means the root is configuring code that belongs to other projects. ## Why Project Isolation rejects it Project Isolation's core rule: a project may only configure **itself**. `allprojects`/`subprojects` violate this by definition — the root mutates siblings. That coupling is what prevents Gradle from configuring projects independently, so isolation reports it as a violation. (It's also fragile without isolation: it depends on evaluation order and makes builds hard to reason about.) ## The replacement: convention plugins Move shared configuration into a **precompiled script plugin** in `buildSrc`: ```kotlin // buildSrc/src/main/kotlin/my.java-conventions.gradle.kts plugins { `java-library` } java { toolchain { languageVersion = JavaLanguageVersion.of(21) } } tasks.withType<Test>().configureEach { useJUnitPlatform() } ``` Then each project opts in: ```kotlin // lib/build.gradle.kts plugins { id("my.java-conventions") } ``` Now: - Each project applies the convention to **itself** — no cross-project mutation. - Projects stay independent, so Gradle can configure them in parallel and cache each separately. - The convention is explicit per project (you can see what applies), testable, and reusable across builds (publish it from an included build if needed). ## Migration tips - Group related conventions into multiple small plugins (java, testing, publishing) rather than one mega-plugin. - Apply them by `id` in each project, not via `apply(from = ...)` of arbitrary scripts. - Avoid putting back any `allprojects`/`subprojects` mutation 'just for one thing' — it reintroduces the violation. ## Net effect Replacing root-driven mutation with per-project convention application is usually the single biggest step in making a build Project-Isolation-compliant.
- Is allprojects {} ever acceptable if it only reads, never mutates?Even reads can pull in another project's mutable model and they encourage coupling; under Project Isolation the safe approach is per-project convention plugins. Prefer eliminating the block entirely.
- Where should a convention plugin live?In buildSrc (auto-available to the build) or in a separate included build you composite in via includeBuild — both let each project apply it by id without cross-project mutation.
It's the difference between a manager personally rearranging everyone's desk (allprojects) versus handing each person the same checklist to set up their own desk (convention plugin) — the latter scales and lets everyone work at once.
saying these in an interview costs you the question
- Saying allprojects/subprojects is fine as long as it's in the root (the root mutating siblings is the violation).
- Replacing the block with cross-project apply(from = ...) hacks instead of real convention plugins.