Class MetricLimits

java.lang.Object
org.ek9lang.compiler.support.MetricLimits

public final class MetricLimits extends Object
THE single source of truth for EK9's enforced complexity / coupling / cohesion / dependency-injection metric limits — the exact thresholds the phase-5 Acceptable*OrError checkers fire on. Use these; do NOT re-declare private static final int MAX_* inside a checker or an extractor.

Why this exists. These limits were previously duplicated: each checker held its own MAX_* constant, and at least one enquiry extractor (DiWiringExtractor.MAX_INJECTION_FIELDS) held a hand-copied mirror with a javadoc pointing back at the checker — a textbook DRY drift with no textual fingerprint to grep. Folding them here means (1) the checker and the MCP/IDE enquiry layer report the same number by construction, and (2) the enforced quality thresholds become discoverable — an AI can ask "what does EK9 enforce, at what value, per genus?" via ek9_query_metric_limits, which just serialises all().

Values and comparators are transcribed verbatim from the checkers (E11014-E11017, E11040, E11042, E11043). Genus tables and their exemptions live with the metric here; the two instance-level LCOM4 exemptions (built-in module, zero-field aggregate) are not genus metadata and remain in AcceptableCohesionOrError.

Four limits are enforced earlier, by the LEXER as it reads the source: BRACKET_NESTING, BLOCK_NESTING, EXPRESSION_OPERATORS and MEMBER_ACCESSES. They bound the shape of the source before anything recurses over it - the parser, its prediction and every later pass - so a pathological source is an error and not a StackOverflowError (front-end audit FE53, batch B13).

  • Field Details

    • MODULE_COUPLING

      public static final MetricLimit.ScalarLimit MODULE_COUPLING
      E11016 — module efferent coupling: distinct external modules a module depends on. Fires above 10.
    • CONSTRUCT_COUPLING

      public static final MetricLimit.GenusLimit CONSTRUCT_COUPLING
      E11015 — construct efferent coupling, per genus. Default (unlisted genera) is the class threshold.
    • LCOM4

      public static final MetricLimit.GenusLimit LCOM4
      E11014 — LCOM4 cohesion, per genus. Traits/records/functions are exempt (single body or intentionally independent operators). Default (unlisted genera) is the class threshold.
    • MODULE_COHESION

      public static final MetricLimit.CompoundLimit MODULE_COHESION
      E11017 — module cohesion: fires only when disconnected groups exceed BOTH an absolute cap (30) and a relative ratio (88% of constructs), and only once the module has at least 60 constructs.
    • MODULE_SIZE

      public static final MetricLimit.ScalarLimit MODULE_SIZE
      E11027 — declared constructs in a single module. Fires above 250.

      Deliberately generous: it is a god-module backstop, not a design nudge (module cohesion E11017 is the real design signal, and fires on unrelated constructs long before a count does). For calibration, 250 is roughly the 99th percentile of real-world Java packages and ~1.6x the largest module in EK9's own corpus; the JDK's own hand-written packages top out around 160.

      Counts SOURCE-DECLARED constructs only. Auto-generated monomorphised parameterised types (List of String and friends) are created in the module that declares the GENERIC, so counting them would let usage elsewhere inflate a module its author does not control - and would make the limit unhittable-by-design for any module exporting a generic. The count comes from ModuleCohesionTracker, which is populated from the parse tree, so synthesised types are excluded by construction rather than by a filter that could drift.

    • INJECTION_FIELDS

      public static final MetricLimit.ScalarLimit INJECTION_FIELDS
      E11040 — injected fields on a single component. Fires above 4 (Single Responsibility smell).
    • APP_REGISTRATIONS

      public static final MetricLimit.ScalarLimit APP_REGISTRATIONS
      E11042 — component registrations in a single application. Fires above 12.
    • INJECTED_TYPES_PER_METHOD

      public static final MetricLimit.ScalarLimit INJECTED_TYPES_PER_METHOD
      E11043 — distinct injected types used within a single method. Fires at or above 5 (last legal 4).
    • CALLABLE_COMPLEXITY

      public static final MetricLimit.ScalarLimit CALLABLE_COMPLEXITY
      E11010 — cyclomatic-style complexity of any callable construct (method / operator / function). Fires above 45.

      Enforced by AcceptableConstructComplexityOrError and re-used by AcceptableCombinedComplexitySizeOrError to normalise its complexity ratio — the two MUST agree for that ratio to mean anything.

    • TYPE_COMPLEXITY

      public static final MetricLimit.ScalarLimit TYPE_COMPLEXITY
      E11010 — aggregated complexity of a type (the sum of its members'). Deliberately large, so only a type accumulating very many even-moderately-complex methods trips it. Fires above 500.
    • COGNITIVE_COMPLEXITY

      public static final MetricLimit.ScalarLimit COGNITIVE_COMPLEXITY
      E11021 — cognitive complexity of a callable. Fires above 35, twice the industry standard of 15: high enough to accommodate genuinely hard code such as parsing, low enough that guarded code at depth 6 (42 points) is over the line.
    • BRACKET_NESTING

      public static final MetricLimit.ScalarLimit BRACKET_NESTING
      E11011 — brackets ((, [ and { together) open inside each other. Enforced by the lexer. Real code nests them 5-6 deep at most; past 3-4 a reader can no longer match them by eye (Steve, 2026-09-28: "10 and 20 - that is more than enough and protects the compiler and makes code readable and maintainable").
    • BLOCK_NESTING

      public static final MetricLimit.ScalarLimit BLOCK_NESTING
      E11011 — blocks (indentation levels) open inside each other, counted from the module. Enforced by the lexer. Real code reaches about 15: module, construct, method, body, then control flow, switch/case, dynamic classes.
    • NESTING_DEPTH

      public static final MetricLimit.ScalarLimit NESTING_DEPTH
      E11011 — nesting depth of control flow within a callable. Fires above 6.
    • INHERITANCE_DEPTH

      public static final MetricLimit.GenusLimit INHERITANCE_DEPTH
      E11019 — inheritance depth, per genus. Slightly stricter than the industry 5-6 because EK9 has by delegation as the alternative. Genera not listed are not depth-checked at all.
    • EXPRESSION_COMPLEXITY

      public static final MetricLimit.ScalarLimit EXPRESSION_COMPLEXITY
      E11013 — complexity of a single expression. Fires above 15, forcing intermediate variables.
    • EXPRESSION_OPERATORS

      public static final MetricLimit.ScalarLimit EXPRESSION_OPERATORS
      E11013 — operators in one expression, which ends at a comma or at the end of its statement (a line ending in a binary operator carries on). Enforced by the lexer. Real code uses at most 15 and 99% of expressions 7 or fewer (census of 5,358 sources, 2026-09-28). Coalescing operators are held far tighter still by EXPRESSION_COMPLEXITY: at most 3 of ??/?:, or 2 of <? >? <=? >=?, in a chain.
    • MEMBER_ACCESSES

      public static final MetricLimit.ScalarLimit MEMBER_ACCESSES
      E11013 — member accesses (.) in one expression, a chain such as a.b().c(). Enforced by the lexer. Real code uses at most 7. The dotted name of a defines module declaration is not a member access.
    • STATEMENTS_OPERATOR

      public static final MetricLimit.ScalarLimit STATEMENTS_OPERATOR
      E11012 — statement budget for an operator. Operators should be single-purpose and brief.
    • STATEMENTS_METHOD

      public static final MetricLimit.ScalarLimit STATEMENTS_METHOD
      E11012 — statement budget for a method; class methods should have a focused responsibility.
    • STATEMENTS_FUNCTION

      public static final MetricLimit.ScalarLimit STATEMENTS_FUNCTION
      E11012 — statement budget for a function; a standalone unit can be somewhat larger.
    • STATEMENTS_SERVICE_OPERATION

      public static final MetricLimit.ScalarLimit STATEMENTS_SERVICE_OPERATION
      E11012 — statement budget for a service operation; a web operation of moderate complexity.
    • ARGUMENTS_PER_CALL

      public static final MetricLimit.ScalarLimit ARGUMENTS_PER_CALL
      E11010 — arguments in a single call. A hard stop, distinct from the 3- and 5-argument points further down that merely ADD to a complexity score rather than failing anything.
    • COMBINED_COMPLEXITY_SIZE

      public static final MetricLimit.RatioLimit COMBINED_COMPLEXITY_SIZE
      E11020 — complexity and size together. Neither operand is a gate on its own: the construct's complexity ratio times its statement ratio must exceed 0.50, so 70% of both passes and 72% of both does not. Expressed as a ratio because there is no count to compare against.
  • Method Details