skip to content

What is a detekt baseline file, how do you generate it, and how should it be used when adopting detekt on a legacy codebase?

level: middleimportance: should knowfreq 48%

answer

  1. Baseline = XML snapshot of current findings; only NEW issues fail
  2. Generate: ./gradlew detektBaseline or --create-baseline
  3. Entry = RuleName:Signature (signature-based)
  4. Burn down over time; don't regenerate every change
  5. Baseline forgives existing; config picks rules — orthogonal

basics

~10 s

A baseline is a file that records all existing detekt findings so they get ignored from now on. It lets you adopt detekt without fixing everything first, while still catching new problems.

solid answer

~40 s

A **baseline** is an XML file (commonly `detekt-baseline.xml`) listing every current finding so detekt suppresses those and only fails on *new* issues introduced after the baseline was taken. You generate it with the `detektBaseline` task (`./gradlew detektBaseline`) or the CLI `--baseline`/`--create-baseline`, and wire it via `baseline.set(file("detekt-baseline.xml"))` in the Gradle `detekt {}` block. Each entry is an `<ID>` of the form `RuleName:Signature`, where the signature identifies the code element (class/function). On later runs detekt diffs findings against this set. Best practice: generate it once at adoption, commit it, then **burn it down** over time rather than regenerating it (regenerating re-absorbs new debt). It is coarser than inline `@Suppress` and signature-based, so it can go stale when code is renamed/moved. Pair it with `buildUponDefaultConfig` so the ruleset that produced the baseline matches future runs.

code

kotlin · 6 lines
kotlin
detekt {
    baseline.set(file("$rootDir/detekt-baseline.xml"))
    buildUponDefaultConfig = true
}
// Create once:  ./gradlew detektBaseline
// Then commit detekt-baseline.xml and burn it down by deleting <ID> lines as you fix them.

go deeper

for a junior

Knows a baseline lets you adopt detekt without fixing all existing issues immediately.

for a middle

Explains generation tasks, the RuleName:Signature format, and the burn-down discipline.

for a senior

Contrasts baseline vs @Suppress vs config, and reasons about staleness on refactors and CI integration.

for a principal

Defines an org policy for baseline ownership, debt burn-down metrics, and preventing silent re-absorption of new debt.

## The adoption problem You switch on detekt for an existing project and get **thousands of findings**. Fixing them all before merging is impractical, but you still want detekt to catch *new* smells. The **baseline** solves exactly this. ## What a baseline is A baseline is an **XML file** (default name `detekt-baseline.xml`) that records a snapshot of current findings: ```xml <?xml version="1.0" ?> <SmellBaseline> <ManuallySuppressedIssues/> <CurrentIssues> <ID>LongMethod:OrderService.kt$OrderService.process(Order)</ID> <ID>MagicNumber:Pricing.kt$Pricing.tax()</ID> </CurrentIssues> </SmellBaseline> ``` Each `<ID>` is `RuleName:Signature`. The **signature** identifies the source element (file, enclosing class, function with parameters). On each run, detekt computes findings, then **subtracts** anything matching a baseline ID — so only *new* findings count. There are two buckets: `CurrentIssues` (auto-captured) and `ManuallySuppressedIssues` (entries you add by hand that detekt won't overwrite on regeneration). ## Generating it ```bash ./gradlew detektBaseline # Gradle task # or CLI detekt --create-baseline --baseline detekt-baseline.xml ``` Wire it in Gradle: ```kotlin detekt { baseline.set(file("$rootDir/detekt-baseline.xml")) buildUponDefaultConfig = true } ``` ## How to use it well - **Generate once** at adoption, commit it, treat it as **technical debt to pay down** ("burn down"). Remove entries as you fix code so the count shrinks. - **Do NOT regenerate on every change** — that re-absorbs new debt and defeats the purpose. Regeneration should be a deliberate, reviewed action. - It is **signature-based**, so renaming a class/function or refactoring can make an entry stale (the finding reappears as "new"). That's actually a feature: touched code gets re-checked. - Prefer **inline `@Suppress("Rule")`** for a permanent, intentional exception in a single spot; use the baseline only for the bulk legacy backlog. ## Relation to config The baseline is orthogonal to `detekt.yml`: config decides *which rules run*, baseline decides *which existing findings are forgiven*. Keep the ruleset stable, or the baseline's IDs won't line up.

  • Why is regenerating the baseline on every CI run an anti-pattern?
    It re-captures new findings as 'forgiven', so freshly introduced smells never fail the build — defeating the gate.
  • When should you prefer @Suppress over the baseline?
    For a deliberate, permanent, localized exception where the intent should be visible at the call site; the baseline is for bulk legacy debt.

A baseline is like marking all known cracks on a wall photo so inspectors only flag NEW cracks — while you slowly patch the old ones off the list.

saying these in an interview costs you the question

  • Regenerating the baseline automatically in CI
  • Thinking the baseline disables rules (it suppresses findings, not rules)
  • Not knowing entries are signature-based and can go stale on refactor
  • Treating the baseline as permanent rather than debt to burn down

context