High Cognitive Complexity

ID

java.cognitive_complexity_methods_high

Severity

high

Remediation Complexity

medium

Remediation Risk

low

Remediation Effort

medium

Resource

Complexity

Language

Java

Tags

complexity, maintainability

Description

Reports a function whose control flow is harder to read than the configured budget — maxCognitiveComplexity, 15 by default. Unlike a raw branch count, this measure weighs where a branch sits: every construct that breaks the normal top-to-bottom reading flow (if, for, while, do, switch, catch, a ternary, a mixed &&/|| condition) costs one point, and each extra level of nesting a construct sits at costs one more. A well-named helper call costs nothing, however much logic it hides.

Rationale

Two functions with the same number of branches are not equally hard to read if one nests them five deep and the other lays them out flat with early returns. Nesting is what actually taxes a reader — at each level they must keep every enclosing condition in mind to know whether the current line even runs. A single if at the top of a method is trivial; the same if buried inside three other conditionals is not, even though a plain branch count treats them the same.

Mixing && and || in one condition pays an extra point on top of the branch itself, because a reader has to stop and work out how the two operators group — precedence makes a || b && c read differently than it groups. A run of the same operator (a && b && c) reads as a single idea and costs only one point.

A well-picked function name is free: calling out to a clearly-named helper costs nothing, no matter how much branching that helper hides. This is what makes "extract a method" the standard fix — it does not remove complexity, it relocates it to a place where the reader can choose not to look at it right now.

equals(Object) and hashCode() overrides are exempt: comparing or hashing every field with a chain of conditions is the idiom for those two methods, not a smell.

class Quote {

  double price(User user, Order order) {                  // FLAW -- cognitive complexity 17
    if (user.isActive()) {                                 // +1  (depth 0)
      if (order.isRush() || order.isFragile()) {           // +2  (depth 1), +1 (one "||" run)
        if (user.hasDiscount() && user.hasCredit()) {      // +3  (depth 2), +1 (one "&&" run)
          for (Item item : order.items()) {                // +4  (depth 3)
            if (item.isTaxed()) {                          // +5  (depth 4)
              return order.total() * 0.9;
            }
          }
        }
      }
    }
    return order.total();
  }

  double simplerPrice(User user, Order order) {           // OK -- flattened, well under the budget
    if (!user.isActive()) {
      return order.total();
    }
    if (!(order.isRush() || order.isFragile())) {
      return order.total();
    }
    return isEligibleForRushDiscount(user) ? order.total() * 0.9 : order.total();
  }

  private boolean isEligibleForRushDiscount(User user) {
    return user.hasDiscount() && user.hasCredit();
  }
}

Remediation

  • Return early. Handle the exceptional or trivial case first and return, instead of wrapping the rest of the method in an if. Each early return removes one level of nesting for everything that follows it.

  • Extract a named predicate. A compound boolean condition promoted to a small, well-named method reads as a sentence at the call site and is scored on its own, separately, wherever it is used.

  • Split the function. A function that legitimately does several distinct things is easier to read — and to test — as several small functions than as one large one with many branches.

Configuration

Property Default Description

maxCognitiveComplexity

15

Cognitive complexity score above which a function is flagged. Raise it for code that is deliberately branch-heavy (a state machine, a parser); lower it to push every non-trivial function towards extraction.

© 2026 Xygeni. All rights reserved