Explain the difference between a project's logical path (the include argument) and its directory on disk.
answer
- logical path = identifier
- directory = bytes on disk
- default maps colon -> folder
- projectDir overrides mapping
- dependencies address by path
basics
~10 sThe include string (e.g. ':services:auth') is a logical project path used to identify the project. By default Gradle maps it to a matching folder (services/auth), but the path and the directory are separate concepts.
solid answer
~40 sAn `include` argument like `:services:auth` is a **logical path** — Gradle's identifier for the project in the build graph and the dependency notation other projects use (`project(":services:auth")`). By default Gradle derives the **directory** from that path: each colon-separated segment becomes a nested folder under the root, so `:services:auth` resolves to `<root>/services/auth`. The two are decoupled: you can keep the logical path but relocate the folder via `project(":services:auth").projectDir = file("backends/auth")` in the settings file. The path also determines the project **name** (last segment, `auth`) and its position in the parent/child hierarchy. Misunderstanding this leads people to think renaming a folder is enough to move a project — it isn't, because dependencies and tasks address projects by logical path.
code
kotlin · 4 lines// settings.gradle.kts
include(":services:auth")
// keep the logical path, relocate the folder
project(":services:auth").projectDir = file("backends/auth-service")go deeper
Recognize that the include string is an identifier and usually matches a folder of the same name.
Cleanly separate logical path from directory; know the default colon→folder mapping and that projectDir overrides it.
Explain that dependencies, the command line, and the hierarchy all key off the path, so relocating files is a projectDir change, not a rename.
Discuss treating the logical path as a stable contract across the codebase so directory reorganizations don't ripple into every dependency declaration.
## Two separate identities Every Gradle project has two distinct identities that beginners conflate: 1. **Logical path** — the string you pass to `include`, e.g. `:services:auth`. This is the project's address in the build graph. Everything else in Gradle refers to a project by this path: `project(":services:auth")` in a dependency block, `:services:auth:build` on the command line, `findProject(":services:auth")` in code. 2. **Project directory** — the folder on disk that holds the project's `build.gradle.kts` and sources. ## Default mapping When you `include(":services:auth")`, Gradle, by default, **derives** the directory by treating each colon as a path separator relative to the root: ``` :services:auth -> <rootDir>/services/auth ``` The last path segment also becomes the project's **name** (`auth`), and the parent segment establishes the hierarchy (`auth`'s parent is the `services` path). ## Decoupling them The mapping is only a default. In the settings file you can override the directory while keeping the logical path stable: ```kotlin include(":services:auth") project(":services:auth").projectDir = file("backends/auth-service") ``` Now the project is still addressed as `:services:auth` everywhere, but its files live in `backends/auth-service`. This is exactly why a flat layout (`includeFlat`, a sibling topic) can register sibling-directory projects without nesting them in the path. ## Practical consequences - **Renaming a folder is not renaming a project.** If you change a directory but not the `include` path, builds break because the default mapping no longer finds the folder; you must either move the folder back or set `projectDir`. - **Dependencies are by path, not folder.** `implementation(project(":services:auth"))` is unaffected by where the folder physically sits. - **The hierarchy is by path.** `:services:auth`'s parent is determined by the colons, not by folder nesting. ## Mental model Think of the logical path as a stable URI and the directory as where the bytes happen to live. The default just keeps them in sync so you rarely think about it — until you intentionally relocate code.
- If I move a project's folder but forget to update the settings file, what breaks?The default path→directory mapping no longer finds the build script, so the build fails to evaluate that project. You must set projectDir to the new location or restore the folder.
- How do other projects reference this one in a dependency?By logical path: implementation(project(":services:auth")). The physical directory is irrelevant to the dependency notation.
saying these in an interview costs you the question
- Claiming the include argument is a filesystem path.
- Assuming you can relocate a project just by moving its folder.
- Thinking the project name is independent of the path's last segment by default.