How do you pin a transitive dependency's version using a `strictly` constraint, and how does it differ from `force`?
answer
- constraints block
- version { strictly() }
- first-class, fails fast
- because() rationale
- publishable in metadata
basics
~20 sAdd a constraint with a strict version: constraints { implementation('g:a') { version { strictly('1.2.3') } } }. Unlike force, it's a first-class version requirement that participates in resolution and fails loudly if something genuinely needs another version.
solid answer
~40 sYou declare a **constraint** in the `dependencies { constraints { … } }` block targeting the module without bringing it in, and set `version { strictly('1.2.3') }`. ```kotlin dependencies { constraints { implementation("org.apache.commons:commons-lang3") { version { strictly("3.12.0") } } } } ``` `strictly` says: this module's version *must* be 3.12.0 (or within the strict range). It downgrades higher transitive requests and participates in conflict resolution as a hard upper bound. If another dependency *strictly* requires an incompatible version, resolution **fails** with a clear message — that fail-fast behaviour is the big difference from `force`, which silently wins. Constraints are also publishable in Gradle Module Metadata, so a platform/BOM can govern versions for consumers. `force` is an opaque, configuration-level override with none of that.
code
kotlin · 8 linesdependencies {
constraints {
implementation("com.fasterxml.jackson.core:jackson-databind") {
version { strictly("2.15.3") }
because("pin transitive jackson to a patched version")
}
}
}go deeper
Know the constraints block exists and that strictly pins a version.
Write the constraint correctly, explain it doesn't add the module, and contrast strictly's fail-fast with force.
Discuss require/prefer/strictly semantics, because() rationale, and endorseStrictVersions for platforms.
Design a BOM/platform that publishes strict constraints as org-wide governance and explain consumer endorsement.
## Constraints vs declarations A **dependency declaration** (`implementation("g:a:v")`) both *adds* the module and states a version. A **constraint** only states *rules* about a module's version — it influences resolution *if and when* the module appears in the graph but never pulls it in by itself. That's why constraints are perfect for pinning **transitive** dependencies you don't depend on directly. ```kotlin dependencies { constraints { implementation("com.fasterxml.jackson.core:jackson-databind") { version { strictly("2.15.3") } because("CVE-2022-xxxx fixed in 2.15.x; pin to avoid drift") } } } ``` ## The version richness: required / preferred / strictly Inside `version { … }` you have three notions: - **`require(...)`** — the lowest acceptable version; the default when you write `"g:a:1.0"`. Higher versions are fine. - **`prefer(...)`** — a soft tie-breaker used only when nothing else decides; never forces a downgrade. - **`strictly(...)`** — a hard constraint: the resolved version *must* satisfy it. It can downgrade and it can cause a hard failure. You can combine: `require("[1.0, 2.0)"); prefer("1.5")`. `strictly` may itself be a range, e.g. `strictly("[1.2, 1.3]")`. ## Why `strictly` beats `force` | Aspect | `force` | `strictly` constraint | |---|---|---| | Nature | opaque config override | first-class version requirement | | Conflict with a genuine need | silently wins | **fails fast** with explanation | | Publishable in metadata | no | yes (platforms/BOMs) | | Carries `because(...)` rationale | no | yes | | Endorse strict from a dependency | n/a | `endorseStrictVersions()` | ## Endorsing strict versions A platform can declare strict versions and a consumer can opt in with `dependencies { implementation(platform("…")) }` plus `endorseStrictVersions()` so the platform's strict versions become the consumer's strict versions.
- What happens if two dependencies declare incompatible `strictly` versions for the same module?Resolution fails with a version conflict error naming both strict requirements and the modules that introduced them — you must reconcile them (e.g. with a substitution or a wider strict range).
- Does a constraint pull the module into the graph if nothing depends on it?No. Constraints only act when the module is already present. To both add and pin, declare the dependency with the version directly.
- What does `because(...)` add?A human-readable rationale that shows up in `dependencies` / `dependencyInsight` reports, documenting why the constraint exists.
saying these in an interview costs you the question
- Saying a constraint adds the dependency to the graph.
- Treating strictly as identical to force with no fail-fast difference.
- Confusing strictly (hard) with prefer (soft tie-breaker).