When you include(":services:auth") but never include(":services"), does a configurable :services project exist?
answer
- leaf created, parent is a path node
- intermediate segment exists structurally
- no build script = nothing to configure
- include parent explicitly to configure it
- namespace vs configurable project
basics
~20 sIncluding only :services:auth creates the auth project and a :services container in the hierarchy, but :services has no build script by default, so it isn't a project you configure. To configure :services, include it explicitly.
solid answer
~50 sWhen you call `include(":services:auth")`, Gradle creates the **leaf** project `:services:auth` and establishes `:services` as its **parent path** in the hierarchy. Gradle does materialize a `:services` project node so the tree is well-formed, but it has **no build script** unless one exists at `services/build.gradle.kts` — and you typically didn't intend one. The practical answer interviewers want: an intermediate path segment exists structurally, but if you want `:services` to be a real, configurable project (apply plugins, add an aggregate task), you should `include(":services")` explicitly, which makes its missing build script optional rather than implicit. Conversely, if `:services` is purely an organizational namespace with nothing to configure, you simply don't give it a build script and never reference it. The key insight: an intermediate segment is part of the path hierarchy, but only an included project with (optionally) a build script is something you configure.
code
kotlin · 6 lines// settings.gradle.kts
// :services becomes a real, configurable project:
include(":services", ":services:auth", ":services:billing")
// vs. purely a namespace (only leaves included):
// include(":services:auth", ":services:billing")go deeper
Know that including :services:auth gives you the auth project; the parent is mostly a grouping.
Explain that the intermediate segment exists as a path node but has no build logic unless a script is present.
Separate path hierarchy, inclusion, and configurability; advise including the parent explicitly when you need to configure it.
Use the path-as-namespace pattern deliberately to organize large builds without proliferating empty configurable projects, and document the convention.
## The scenario ```kotlin include(":services:auth") // note: no include(":services") ``` The question is whether `:services` is a usable, configurable project. The honest, nuanced answer: ## What Gradle does with the parent segment Project paths form a strict tree under the root. For `:services:auth` to slot into that tree, the path `:services` must exist as a node — Gradle creates the intermediate project descriptor so the hierarchy is well-formed (`:services` is `:auth`'s parent, the root is `:services`'s parent). So *structurally* `:services` exists. What it does **not** automatically get is meaningful content: - By default Gradle looks for `services/build.gradle.kts`. If absent, `:services` is an empty project — it has no plugins, no tasks of its own beyond Gradle's defaults, nothing you configured. - It is not where you'd hang real build logic unless you deliberately set it up. ## When to include the parent explicitly Include `:services` explicitly when you want it to be a genuine project you configure: ```kotlin include(":services", ":services:auth", ":services:billing") ``` Useful when `:services` should, e.g., apply a convention plugin to all its children via `subprojects { }`, or expose an aggregate task. Being explicit documents intent and makes the (often absent) `services/build.gradle.kts` clearly optional rather than surprising. ## When to leave it implicit If `:services` is purely a **namespace** — a way to group related leaves under a tidy path — you don't need to configure it at all. You include only the leaves, never reference `:services`, and don't create a build script for it. The grouping shows up in `./gradlew projects` and in dependency paths without any extra ceremony. ## The principle Separate three notions: 1. **Path hierarchy** — derived from colons; intermediate segments exist as nodes so the tree is valid. 2. **Inclusion** — which leaves you actually registered via `include`. 3. **Configurability** — whether a project has a build script you put logic in. An intermediate segment participates in (1) automatically, but you decide (2) and (3). If you need to *do* something at `:services`, include it and give it a build script; if it's just organizational, leave it alone. ## Why interviewers ask It probes whether you understand that the project path is a hierarchy independent of where build logic lives — the same decoupling that underlies flat layouts and custom `projectDir`.
- How would you apply a convention plugin to every project under :services?Include :services explicitly and use subprojects { } (or, better, a convention plugin) in its build script, or configure children from the settings/root level. Without including :services you'd target the leaves directly.
- Does ./gradlew projects show the intermediate :services segment?Yes — the hierarchy reflects the path, so :services appears as a parent grouping the included leaves even if it has no build script.
saying these in an interview costs you the question
- Claiming including a leaf gives you a fully configurable parent project automatically with a build script.
- Saying the parent segment doesn't exist at all in the hierarchy.
- Confusing path nesting with where build logic must live.