E11020: combined complexity and size exceeds maintainability threshold

Category: E11xxx Code quality and IR analysis · Reported in phase: PRE_IR_CHECKS · Classification: COMBINED_COMPLEXITY_SIZE · All error codes

What went wrong

While complexity and statement count individually pass their limits, the COMBINATION exceeds the maintainability threshold. Both metrics are borderline high, creating compound risk.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 1339 (Why does EK9 reject a function whose complexity and si...)

Rationale

  • Formula: (complexity/maxComplexity) × (statements/maxStatements)
  • Statements are counted as for E11012: declarations, assignments, control flow, throw, calls, increments, streams, and require / assert / assertThrows / assertDoesNotThrow / assertPanic
  • Threshold: 0.50 (e.g., 69% × 71% = 0.49 passes, 71% × 72% = 0.51 fails)
  • Based on NASA SATC research on module reliability: modules with both high complexity and large size have the lowest reliability (Rosenberg, Hammer & Shaw, "Software Metrics and Reliability", ISSRE 1998)

Example scenario

  • Complexity at 75% of limit (passes E11010)
  • Statements at 75% of limit (passes E11012)
  • Combined: 0.75 × 0.75 = 0.56 → FAILS E11020

How to fix it

  1. Extract helper functions (reduces statements, may reduce complexity)
  2. Simplify conditional logic (reduces complexity)
  3. Use polymorphism instead of switch/if chains (reduces both)
  4. TARGET: Both metrics below 70% of limits → combined ≤0.49

Common causes

  • Function accumulated both logic and size over time
  • Multiple responsibilities merged into single function

See also

  • E11020: combined complexity × size (this error)
  • E11010: cyclomatic complexity alone
  • E11012: statement count alone

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E11020: combined complexity and size exceeds maintainability threshold

#!ek9
defines module error.e11020.bad

  defines function

    combinedTooHigh()
      ->
        counter1 as Integer
        counter2 as Integer
        counter3 as Integer
        counter4 as Integer
        counter5 as Integer
      <-
        rtn as Integer: 0

      //90 variable declarations (each counts as 1 statement)
      v1 <- 1
      v2 <- v1 + 1
      v3 <- v2 + 1
      v4 <- v3 + 1
      v5 <- v4 + 1
      v6 <- v5 + 1
      v7 <- v6 + 1
      v8 <- v7 + 1
      v9 <- v8 + 1
      v10 <- v9 + 1
      v11 <- v10 + 1
      v12 <- v11 + 1
      v13 <- v12 + 1
      v14 <- v13 + 1
      v15 <- v14 + 1
      v16 <- v15 + 1
      v17 <- v16 + 1
      v18 <- v17 + 1
      v19 <- v18 + 1
      v20 <- v19 + 1
      v21 <- v20 + 1
      v22 <- v21 + 1
      v23 <- v22 + 1
      v24 <- v23 + 1
      v25 <- v24 + 1
      v26 <- v25 + 1
      v27 <- v26 + 1
      v28 <- v27 + 1
      v29 <- v28 + 1
      v30 <- v29 + 1
      v31 <- v30 + 1
      v32 <- v31 + 1
      v33 <- v32 + 1
      v34 <- v33 + 1
      v35 <- v34 + 1
      v36 <- v35 + 1
      v37 <- v36 + 1
      v38 <- v37 + 1
      v39 <- v38 + 1
      v40 <- v39 + 1
      v41 <- v40 + 1
      v42 <- v41 + 1
      v43 <- v42 + 1
      v44 <- v43 + 1
      v45 <- v44 + 1
      v46 <- v45 + 1
      v47 <- v46 + 1
      v48 <- v47 + 1
      v49 <- v48 + 1
      v50 <- v49 + 1
      v51 <- v50 + 1
      v52 <- v51 + 1
      v53 <- v52 + 1
      v54 <- v53 + 1
      v55 <- v54 + 1
      v56 <- v55 + 1
      v57 <- v56 + 1
      v58 <- v57 + 1
      v59 <- v58 + 1
      v60 <- v59 + 1
      v61 <- v60 + 1
      v62 <- v61 + 1
      v63 <- v62 + 1
      v64 <- v63 + 1
      v65 <- v64 + 1
      v66 <- v65 + 1
      v67 <- v66 + 1
      v68 <- v67 + 1
      v69 <- v68 + 1
      v70 <- v69 + 1
      v71 <- v70 + 1
      v72 <- v71 + 1
      v73 <- v72 + 1
      v74 <- v73 + 1
      v75 <- v74 + 1
      v76 <- v75 + 1
      v77 <- v76 + 1
      v78 <- v77 + 1
      v79 <- v78 + 1
      v80 <- v79 + 1
      v81 <- v80 + 1
      v82 <- v81 + 1
      v83 <- v82 + 1
      v84 <- v83 + 1
      v85 <- v84 + 1
      v86 <- v85 + 1
      v87 <- v86 + 1
      v88 <- v87 + 1
      v89 <- v88 + 1
      v90 <- v89 + 1

      //18 if statements with comparisons (each if adds +1 complexity, each > adds +1 complexity)
      //Each if is also a statement
      if counter1 > 0
        v1 := v1 + 1

      if counter2 > 0
        v2 := v2 + 1

      if counter3 > 0
        v3 := v3 + 1

      if counter4 > 0
        v4 := v4 + 1

      if counter5 > 0
        v5 := v5 + 1

      if v1 > v2
        v6 := v6 + 1

      if v2 > v3
        v7 := v7 + 1

      if v3 > v4
        v8 := v8 + 1

      if v4 > v5
        v9 := v9 + 1

      if v5 > v6
        v10 := v10 + 1

      if v6 > v7
        v11 := v11 + 1

      if v7 > v8
        v12 := v12 + 1

      if v8 > v9
        v13 := v13 + 1

      if v9 > v10
        v14 := v14 + 1

      if v10 > v11
        v15 := v15 + 1

      if v11 > v12
        v16 := v16 + 1

      if v12 > v13
        v17 := v17 + 1

      if v13 > v14
        v18 := v18 + 1

      //Final assignment reads the eighteen updated above; the rest feed one another up to v90
      rtn := v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15 + v16 + v17 + v18 + v90

Fixed: Split a long, branching function into focused steps

#!ek9
defines module error.e11020.good

  defines function

    validated()
      ->
        first as Integer
        second as Integer
      <- rtn as Boolean: first > 0 and second > 0

    combined()
      ->
        first as Integer
        second as Integer
      <- rtn as Integer: first + second

    combinedWithinLimits()
      ->
        first as Integer
        second as Integer
      <- rtn as Integer: 0

      if validated(first, second)
        rtn: combined(first, second)