How do you compose multiple convention plugins on a single module, and how do you decide which conventions a given module should apply?
answer
- stack ids in one plugins {} block
- base java-conventions + role presets
- smallest set matching role
- avoid mega catch-all plugin
- later plugin wins on shared extension
basics
~10 sList several plugin ids in the module's plugins {} block — e.g. a base java-conventions plus a more specific application-conventions or library-conventions. The module applies only the presets matching its role.
solid answer
~40 sConvention plugins compose by stacking ids in one `plugins { }` block. A common layout is a small base plugin (`myproject.java-conventions`: toolchain, repositories, test deps) plus role-specific plugins built on top (`myproject.library-conventions`, `myproject.application-conventions`, `myproject.spring-conventions`). A leaf module applies the set that matches what it *is*: a Spring web service might apply `java-conventions` + `spring-conventions` + `application-conventions`. The decision rule is by responsibility, not by 'all modules get everything'. You pick the smallest set of presets that captures the module's role. Because application is explicit, the plugins block doubles as documentation of the module's nature. Avoid one giant catch-all convention plugin applied everywhere — that recreates the subprojects {} problem of forcing identical config on heterogeneous modules.
code
kotlin · 12 lines// A publishable library module
plugins {
id("myproject.java-conventions")
id("myproject.library-conventions")
}
// An executable service module
plugins {
id("myproject.java-conventions")
id("myproject.spring-conventions")
id("myproject.application-conventions")
}go deeper
Know you can list more than one id in plugins {}.
Describe base + role-specific layering and how to pick the minimal matching set.
Discuss ordering, extension-override semantics, and avoiding the mega-convention anti-pattern.
Define the org's catalog of convention plugins and governance over how teams compose them.
## Composition is just listing ids Gradle's `plugins { }` block applies multiple plugins in order: ```kotlin // service/build.gradle.kts plugins { id("myproject.java-conventions") id("myproject.spring-conventions") id("myproject.application-conventions") } ``` Each id pulls in its own bundle of config. When a precompiled convention plugin internally applies another (`myproject.application-conventions` may itself apply `java`), those layer together. The applying module ends up with the union of everything, configured against its own project. ## A typical layering - **`java-conventions`** — base: Java toolchain, `mavenCentral()`, common test dependencies (JUnit), warnings-as-errors. Almost every JVM module applies this. - **`library-conventions`** — adds publishing setup, API/implementation hygiene, maybe `java-library`. - **`application-conventions`** — adds the `application` plugin, main-class wiring, run config. - **`spring-conventions`** — adds the Spring Boot/dependency-management plugins and BOM. These are *examples* of how application stacks; the point is that a module assembles its config from named building blocks. ## How to decide what a module applies Ask: *what is this module's role?* 1. Start from the base every JVM module needs (`java-conventions`). 2. Add exactly the role presets that match: is it a publishable library? an executable app? a Spring service? 3. Add nothing it doesn't need. The result is the **smallest set of presets that captures the module's responsibilities**. The `plugins { }` block then reads almost like a description of the module. ## Anti-pattern: the mega-convention A tempting shortcut is one `everything-conventions` plugin applied to all modules. That brings back the `subprojects {}` pathology: heterogeneous modules forced into identical config, a leaf library dragging in the `application` plugin it doesn't want, etc. Prefer several focused plugins composed à la carte. ## Ordering note Plugin application order can matter if one plugin's config reacts to another being present. Generally apply the base first, then specializations. If two plugins both configure the same extension, the later one wins for any value it sets — so keep responsibilities non-overlapping where possible. ## Summary Composition = stack ids; selection = match the module's role with the minimal set of presets. This à-la-carte model is exactly what `subprojects {}` cannot offer, since that block applies one configuration to everyone.
- Does application order of convention plugins matter?It can. Apply base before specializations; if two plugins set the same extension value, the later application wins. Keeping plugin responsibilities non-overlapping avoids surprises.
- Why not just make one big convention plugin everyone applies?Because it forces identical config on heterogeneous modules, recreating the subprojects {} problem — a library would inherit the application plugin it doesn't need.
saying these in an interview costs you the question
- Recommending a single all-modules convention plugin as best practice.
- Believing only one convention plugin can be applied per module.