Which code metrics does detekt's complexity rule set measure, and what are its default limits?
answer
- Counting rules, not correctness rules
- Branching, lines, parameters, member counts
- One of the two complexity rules is off by default
- Roughly 60 lines a function, 600 a class
- Keys renamed to allowed* in detekt 2.0
basics
~10 sdetekt's complexity set measures branching and size: CyclomaticComplexMethod and CognitiveComplexMethod per function, LongMethod and LargeClass in lines, NestedBlockDepth, ComplexCondition, LongParameterList, TooManyFunctions. Defaults sit near 60 lines per function and 600 per class.
solid answer
~40 sThe `complexity` rule set is detekt's numeric layer. Per function it measures **branching** — `CyclomaticComplexMethod` (default allowed complexity 14) and the optional `CognitiveComplexMethod`, which is off by default and penalises *nested* control flow more heavily — plus `ComplexCondition` for boolean operators in a single condition and `NestedBlockDepth` (default 4). Per size it measures `LongMethod` (60 lines) and `LargeClass` (600 lines). Per shape it measures `LongParameterList` (5 for functions, 6 for constructors, data classes ignored by default), `TooManyFunctions` (11 per file, class, interface, object or enum, tests excluded), plus the off-by-default `MethodOverloading` and `ComplexInterface`. Every rule carries options that encode legitimate idioms, so the interesting work is choosing the number your codebase can actually hold rather than accepting or deleting the rule.
code
yaml · 14 linescomplexity:
CyclomaticComplexMethod:
allowedComplexity: 18
LongMethod:
allowedLines: 70
NestedBlockDepth:
allowedDepth: 4
LongParameterList:
allowedFunctionParameters: 5
allowedConstructorParameters: 8
ignoreDataClasses: true
TooManyFunctions:
allowedFunctionsPerClass: 15
ignorePrivate: truego deeper
Recall that the complexity set counts branches, lines, parameters and members, and that the numbers live in detekt.yml. Naming three rules and roughly where the defaults sit is enough here.
Explain what each metric actually counts and why Kotlin-specific options exist — scope functions in the cyclomatic count, data classes in the parameter list, test excludes on function counts.
Show that you have set these numbers for a real codebase: which idioms forced a change, how you kept the limit stable afterwards, and why a green complexity report is a floor rather than a design review.
Own the standard across teams: which metrics are non-negotiable, which are advisory, and how a limit gets changed — by argued decision with a recorded reason, not by whoever hits the red build first.
## What this rule set is for detekt's `complexity` rule set is the part that counts things. It does not reason about correctness; it measures shape — how much branching a function contains, how long it is, how many parameters and members a declaration carries — and reports whatever exceeds a configured number. That makes it the most opinionated and most argued-about part of a detekt configuration, and the reason interviewers ask about it: the numbers encode a team's craft standard. ## Branching metrics, per function **`CyclomaticComplexMethod`** counts independent paths through a function: each `if`, `when` branch, loop, `catch`, and each `&&`/`||` adds one. The default allows a complexity of 14. It is deliberately generous — a function past that is usually a state machine that wants decomposition. The rule has options that matter in Kotlin specifically: `ignoreSingleWhenExpression`, `ignoreSimpleWhenEntries`, `ignoreNestingFunctions` and a `nestingFunctions` list (`let`, `run`, `apply`, `also`, `with`, `use`, `forEach`), because idiomatic Kotlin chains scope functions in ways a Java-shaped metric over-penalises. **`CognitiveComplexMethod`** implements a different metric: instead of weighting every branch equally, it adds a penalty proportional to nesting depth and discounts constructs that read linearly. Three sequential `if`s score far less than three nested ones, which matches how a reader actually experiences the code. It is **inactive by default**, so a team that wants it must switch it on explicitly. **`ComplexCondition`** flags a single boolean expression built from too many operators (default: more than three), and **`NestedBlockDepth`** flags control structures nested past the allowed depth (default 4). These two catch the readability problems the path count alone can miss. ## Size and shape metrics **`LongMethod`** and **`LargeClass`** count source lines — 60 and 600 by default. They are crude on purpose: a long function is not automatically wrong, but it is always worth a second look. **`LongParameterList`** allows 5 parameters on a function and 6 on a constructor, and by default ignores data classes, since a data class *is* a parameter list. `ignoreDefaultParameters` and an `ignoreAnnotatedParameter` list cover injection and framework-driven signatures. **`TooManyFunctions`** counts declarations per container — file, class, interface, object and enum each get their own allowance, 11 by default — and excludes test sources by default, because a test class legitimately holds many small functions. Its `ignorePrivate`, `ignoreInternal`, `ignoreOverridden` and `ignoreDeprecated` switches decide what counts as public surface. Two more ship inactive: **`MethodOverloading`** (too many overloads of one name) and **`ComplexInterface`** (too many declarations in one interface). ## How the limits are spelled This is version-sensitive and worth stating explicitly in an interview. detekt 1.23 spells these limits with `threshold`-style keys — `LongMethod: threshold: 60`, `TooManyFunctions: thresholdInFiles: 11` — with the semantics *report when the metric reaches the threshold*. detekt 2.0 renames them to intent-revealing keys that express what is **allowed**: `allowedLines`, `allowedComplexity`, `allowedDepth`, `allowedConditions`, `allowedFunctionParameters`, `allowedConstructorParameters`, `allowedFunctionsPerFile` / `PerClass` / `PerInterface` / `PerObject` / `PerEnum`. Because the meaning shifted from *threshold reached* to *maximum permitted*, re-read the numbers when you upgrade rather than copying them across blindly. ## Using the numbers well The defaults are a starting position, not a verdict. A codebase full of exhaustive `when` dispatch over a sealed hierarchy will trip cyclomatic complexity for a structure that is genuinely readable; a Spring-style constructor will trip the parameter list. The mature move is to pick a number the team can hold and then hold it — raising a limit once, with a comment explaining the codebase's idiom, is a standard; raising it every time the build goes red is an unmaintained rule. Equally, these metrics are a smoke detector, not a design review. Passing every complexity rule says nothing about coupling, naming or module boundaries. Cite them as a floor under review, never as a substitute for it.
- How does CognitiveComplexMethod differ from CyclomaticComplexMethod, and why would you enable it?Cyclomatic complexity weights every branch equally, so three sequential `if`s score the same as three nested ones. Cognitive complexity adds a penalty that grows with nesting depth and discounts constructs that read linearly, which tracks reading difficulty far better. It is inactive by default, so enabling it is a deliberate choice — often alongside a *lower* number than the cyclomatic one.
- Why does LongParameterList ignore data classes by default?A data class is essentially a named parameter list: the constructor's job is to carry fields, and splitting it would just move the same data behind another type. detekt encodes that with `ignoreDataClasses: true`. The companion options `ignoreDefaultParameters` and `ignoreAnnotatedParameter` exist for the same reason — framework-driven signatures are not the design smell the rule is hunting.
- Do any complexity rules exclude test sources out of the box?`TooManyFunctions` ships with test-source path globs in its `excludes` list, because a test class legitimately holds many small functions. Most other complexity rules apply everywhere by default, so if long test setup functions are a deliberate style you scope them yourself with the rule's own `excludes` list rather than turning the rule off.
saying these in an interview costs you the question
- Calling every complexity finding a bug to fix
- Assuming cognitive complexity is on by default
- Thinking lines of code and cyclomatic complexity measure the same thing
- Copying 1.23 threshold numbers into 2.0 allowed* keys unchanged
- Treating a green complexity report as a passed design review