How does `detekt.yml` work, and what do `buildUponDefaultConfig` and `--build-upon-default-config` mean when configuring rules?
answer
- Hierarchy: rule set -> rule -> properties (active, thresholds)
- config.setFrom REPLACES defaults; buildUponDefaultConfig MERGES on top
- CLI flag: --build-upon-default-config
- config.validation: true catches misspelled keys
- detektGenerateConfig writes a full starter file
basics
~10 sdetekt.yml is a YAML file where you turn rules on or off and set their thresholds. With buildUponDefaultConfig, your file only overrides the defaults instead of replacing them entirely.
solid answer
~40 s`detekt.yml` is the YAML configuration that controls which rules run and how strict they are. It is organized by rule set (`complexity:`, `style:`, etc.), then by rule, each with keys like `active: true/false`, thresholds (e.g. `LongMethod: threshold: 60`), and per-rule options. By default detekt ships a complete default config; if you point `config.setFrom(...)` at your own file, it **replaces** the defaults — so any rule you omit becomes inactive. Setting `buildUponDefaultConfig = true` (or the CLI `--build-upon-default-config`) instead *layers* your file on top of the bundled defaults, so you only specify deltas. There are also global keys: `build.maxIssues` (fail threshold), `config.validation` (reject unknown keys), and `excludes`/`includes` path globs. Generate a starter with `./gradlew detektGenerateConfig`. Inline suppression uses `@Suppress("RuleName")` or `@Suppress("detekt:RuleName")`.
code
kotlin · 14 lines// build.gradle.kts
detekt {
buildUponDefaultConfig = true // layer my file over defaults
config.setFrom(files("$rootDir/detekt.yml"))
allRules = false
}
// detekt.yml (delta only)
// complexity:
// LongMethod:
// threshold: 80
// style:
// MagicNumber:
// active: falsego deeper
Knows detekt.yml turns rules on/off and is YAML with active flags.
Explains the replace-vs-merge behavior of buildUponDefaultConfig and tuning thresholds.
Adds config.validation, path excludes, generated config workflow, and inline @Suppress vs baseline trade-offs.
Designs a shared/base config strategy across modules and reasons about drift, validation, and review of config changes.
## The shape of `detekt.yml` `detekt.yml` is a YAML file with a fixed hierarchy: **rule set → rule → properties**. ```yaml complexity: active: true LongMethod: active: true threshold: 60 # max lines before it complains CyclomaticComplexMethod: threshold: 15 style: MagicNumber: active: true ignoreNumbers: ['-1', '0', '1', '2'] MaxLineLength: maxLineLength: 120 naming: FunctionNaming: functionPattern: '[a-z][a-zA-Z0-9]*' ``` Every rule supports `active: true|false`. Many add tuning keys (thresholds, patterns, ignore lists). ## Default config vs your config — the key gotcha detekt ships a **complete default config** baked into the jar. Two modes decide how your file relates to it: - **Replace (default when you set a config file):** `config.setFrom(files("detekt.yml"))` makes detekt use *only* your file. **Any rule you don't mention is treated as not configured** — effectively off for rules that default to active only in the bundled config. - **Build upon defaults:** set `buildUponDefaultConfig = true` (Gradle) or pass `--build-upon-default-config` (CLI). Now detekt starts from the bundled defaults and **merges your file on top**, so you only write the *deltas* you care about. This is the recommended setup for most teams. ```kotlin detekt { buildUponDefaultConfig = true config.setFrom(files("$rootDir/detekt.yml")) } ``` ## Global / framework keys Beyond rules, the file has top-level sections: ```yaml build: maxIssues: 0 # >0 findings fails the build config: validation: true # error on unknown/misspelled keys warningsAsErrors: false ``` `config.validation` is valuable: it catches typos like `actve:` that would otherwise silently disable a rule. ## Generating and excluding - `./gradlew detektGenerateConfig` writes a full annotated `detekt.yml` you can trim. - Per-rule path filters: `excludes: ['**/test/**']` to skip test sources. ## Suppressing in code Use `@Suppress("MagicNumber")` on a declaration, or the prefixed form `@Suppress("detekt:MagicNumber")` to be explicit. Baselines (separate file) handle bulk legacy suppression instead. ## Why it matters Forgetting `buildUponDefaultConfig` is the classic mistake: a team writes a small `detekt.yml`, and suddenly most rules go silent because the default ruleset was replaced rather than extended.
- Why might a team find half their rules silently disabled after adding a custom detekt.yml?They set their own config without buildUponDefaultConfig, so their file replaced the bundled defaults; rules they didn't list became inactive.
- How do you suppress a single finding inline?Annotate the declaration with @Suppress("RuleName") (optionally @Suppress("detekt:RuleName")).
saying these in an interview costs you the question
- Believing a custom config always merges with defaults automatically
- Not knowing config.validation exists to catch typos
- Confusing detekt.yml rule disabling with the baseline mechanism
- Thinking thresholds (e.g. LongMethod) cannot be tuned