How does EK9 measure complexity?

← Code Quality · Ref: Q312

EK9 measures complexity using five metrics, all enforced at compile time. Exceeding any threshold is a compilation error, not a warning.

CYCLOMATIC COMPLEXITY (E11010)

Measures the number of independent paths through a function. Each if, else if, while, for, switch case, and guard adds one. Threshold: 45. Based on McCabe 1976 research showing defect rate increases sharply above this level.

NESTING DEPTH (E11011)

Measures the deepest level of nesting in a function. Each nested if, for, while, switch, or try adds one level. Threshold: 6. Deep nesting correlates with bugs because humans lose track of context beyond 3-4 levels.

STATEMENT COUNT (E11012)

Counts executable statements in a function. Long functions are harder to understand, test, and modify. The threshold encourages decomposition into smaller, focused functions.

COMBINED COMPLEXITY (E11020)

A product formula: (complexity / maximum complexity) x (statements / maximum statements) must not exceed 0.50. This catches functions that are individually below each threshold but moderately high on both measures. A function with 40 cyclomatic complexity and a high statement count can still be flagged. Nesting depth is not part of this formula (it has its own limit, E11011).

COGNITIVE COMPLEXITY (E11021)

Measures how hard code is to understand, not just how many paths exist. Cognitive complexity penalises nesting more heavily than flat structures. A deeply nested if-else chain scores higher than the same logic refactored into flat guards. Threshold: 35.

WHAT ADDS COMPLEXITY

Each of these adds to complexity:
- if/else if/else branches
- for/while loops
- switch cases
- try/catch handlers
- guards (but less than nested ifs)
- Boolean operators in conditions

DECOMPOSITION PATTERN

When a function is too complex, extract helper functions. Each extracted function has its own complexity budget. This is exactly the decomposition that EK9 philosophy encourages.

See Q311 for the full quality checks catalog. See Q313 for code smell detection. See Q146 for decomposition over return. See Q630 for identifying hot methods via profiling.

Example

defines module qa.codequality.complexity

  defines function

    <?-
      A well-decomposed function stays within complexity thresholds.
      Each helper handles one concern with low individual complexity.
    -?>
    isWeekday() as pure
      -> dayNumber as Integer
      <- rtn as Boolean: false

      mondayIndex <- 1
      fridayIndex <- 5
      if dayNumber >= mondayIndex and dayNumber <= fridayIndex
        rtn: true

    describeDayType() as pure
      -> dayNumber as Integer
      <- description as String: "weekend"

      if isWeekday(dayNumber)
        description: "weekday"

    formatDayReport() as pure
      ->
        dayName as String
        dayNumber as Integer
      <-
        report as String: ""

      dayType <- describeDayType(dayNumber)
      report: `${dayName} is a ${dayType}`

  defines program

    ComplexityMetricsDemo()
      stdout <- Stdout()

      mondayNumber <- 1
      saturdayNumber <- 6

      mondayReport <- formatDayReport("Monday", mondayNumber)
      saturdayReport <- formatDayReport("Saturday", saturdayNumber)

      stdout.println(mondayReport)
      stdout.println(saturdayReport)

Common mistakes

E50001 — A misspelt variable (mondayNumberXYZ) at its use does not resolve, triggering E50001. See ek9 -h E50001 for details.

Incorrect:

mondayReport <- formatDayReport("Monday", mondayNumberXYZ)

Correct:

mondayReport <- formatDayReport("Monday", mondayNumber)

E50001 — A misspelt variable (saturdayNumberXYZ) at its use does not resolve, triggering E50001. See ek9 -h E50001 for details.

Incorrect:

saturdayReport <- formatDayReport("Saturday", saturdayNumberXYZ)

Correct:

saturdayReport <- formatDayReport("Saturday", saturdayNumber)
Other ways to ask this
  • What complexity metrics does EK9 use?
  • How does EK9 calculate cyclomatic complexity?
  • What is cognitive complexity in EK9?

Coming from another language?

Java: SonarQube measures cyclomatic and cognitive complexity but as optional warnings. PMD has CyclomaticComplexity rule but configurable threshold. Rust: no built-in complexity measurement, clippy has cognitive_complexity lint (allow by default). Go: gocyclo is a third-party tool, not built-in. Python: radon measures complexity but is a separate optional tool. EK9: all five complexity metrics built into the compiler as mandatory errors with fixed thresholds.

Keywords: metric, metrics, nesting, quality, complexity, E11010, E11011, cyclomatic, E11021, E11020, clean-code, threshold, cognitive, mccabe, depth