skip to content

How does `detekt.yml` work, and what do `buildUponDefaultConfig` and `--build-upon-default-config` mean when configuring rules?

level: middleimportance: must knowfreq 55%

answer

  1. Hierarchy: rule set -> rule -> properties (active, thresholds)
  2. config.setFrom REPLACES defaults; buildUponDefaultConfig MERGES on top
  3. CLI flag: --build-upon-default-config
  4. config.validation: true catches misspelled keys
  5. detektGenerateConfig writes a full starter file

basics

~10 s

detekt.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
kotlin
// 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: false

go deeper

for a junior

Knows detekt.yml turns rules on/off and is YAML with active flags.

for a middle

Explains the replace-vs-merge behavior of buildUponDefaultConfig and tuning thresholds.

for a senior

Adds config.validation, path excludes, generated config workflow, and inline @Suppress vs baseline trade-offs.

for a principal

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

context