Compile-Time Code Quality Enforcement
Either Good Code or Errors, Never Warnings
EK9 is the first and only mainstream programming language to enforce comprehensive code quality metrics at compile-time. While other languages rely on optional external tools (linters, static analyzers, code quality platforms), EK9 integrates quality enforcement directly into the compiler, making it impossible to compile poor-quality code.
The EK9 Philosophy: If your code compiles, it has passed all quality checks. There are no warnings to ignore, no thresholds to configure, and no external tools to integrate. Quality is enforced by the compiler itself.
This revolutionary approach eliminates technical debt at its source. You cannot accumulate warnings that "will be fixed later" because there are no warnings — only errors that prevent compilation until the code meets quality standards.
Use the -ve (verbose errors) flag to get detailed explanations of any quality
violations. See Command Line for more options.
The Quality Pyramid
EK9 enforces quality at five distinct levels, forming a comprehensive quality pyramid. All five layers must pass for code to compile successfully.
+-----------------------+
| Duplicate Code | <- Codebase-level
| Detection (FUTURE) | (DRY principle)
+-----------------------+
^
|
+-----------------------+
| Inheritance Depth | <- Hierarchy-level
| Limits (E11019) | (composition > inheritance)
+-----------------------+
^
|
+-----------------------+
| Cohesion/Coupling | <- Architecture-level
| (E11014, E11015) | (SOLID principles)
+-----------------------+
^
|
+-----------------------+
| Complexity Limits | <- Function-level
| (E11010-E11013) | (cognitive load)
+-----------------------+
^
|
+-----------------------+
| Purity and Safety | <- Expression-level
| (pure keyword) | (correctness)
+-----------------------+
Layer 1: Purity and Safety (Expression-Level)
The foundation of EK9's quality model. Pure functions have no side effects, making code
predictable and testable. The pure keyword enforces this at the expression level.
Combined with EK9's tri-state semantics and guard expressions,
this eliminates entire categories of bugs.
Layer 2: Complexity Limits (Function-Level)
Functions and methods have complexity limits based on cyclomatic complexity. This keeps cognitive load manageable and encourages decomposition into smaller, focused units. See E11010, E11011, and E11013.
Layer 3: Cohesion and Coupling (Architecture-Level)
Classes must maintain high cohesion (methods work together on related data) and low coupling (limited dependencies on external types). This enforces SOLID principles at the architecture level. See E11014 and E11015.
Layer 4: Inheritance Depth (Hierarchy-Level)
Deep inheritance hierarchies make code hard to understand and maintain.
EK9 limits inheritance depth and encourages composition via delegation
using the by keyword. See E11019.
Layer 5: Duplicate Code Detection (Codebase-Level)
Coming in 2026: IR-based similarity detection will identify duplicated code blocks across your codebase, enforcing the DRY (Don't Repeat Yourself) principle at compile-time.
Why Compile-Time Enforcement?
The Industry Standard Problem
Traditional languages rely on a fragmented ecosystem of optional external tools:
- Checkstyle/StyleCop for style checking (optional, configurable, ignorable)
- SonarQube/NDepend for complexity and quality (requires setup, often ignored)
- Snyk/Fortify for security scanning (external, expensive)
- Code review for design quality (subjective, slow, inconsistent)
- CI/CD gates for enforcement (can be bypassed, creates friction)
This results in fragmented, optional, bypassable quality enforcement. Warnings accumulate, tools are misconfigured, and technical debt compounds over time.
The EK9 Solution
EK9 replaces this entire fragmented ecosystem with one tool: the compiler.
- No configuration — thresholds are built into the language
- No external tools — quality checks run during compilation
- No warnings — either it compiles (passed) or it doesn't (failed)
- No bypass — quality is mandatory, not optional
The "No Gray Area" Principle
Traditional Approach:
+-- Compiles (green)
+-- Compiles with warnings (gray area - technical debt zone)
+-- Doesn't compile (red)
EK9 Approach:
+-- Compiles (passed ALL checks) (green)
+-- Doesn't compile (red)
No gray area. No accumulating debt.
Implemented Quality Metrics
EK9 currently enforces four categories of quality metrics. Each violation produces a compile-time error with detailed guidance on how to fix the issue.
4.1 Complexity Limits
Complexity is measured using cyclomatic complexity (McCabe, 1976). High complexity indicates code that is difficult to understand, test, and maintain.
| Error Code | Metric | Threshold | Description |
|---|---|---|---|
| E11010 | Cyclomatic Complexity | Function: 45, Class: 500 | Too many decision points (if/switch/loops) |
| E11011 | Nesting Depth | Max: 6 levels | Absolute structural limit for nested control structures |
| E11021 | Cognitive Complexity | Max: 35 points | Guards (<-) add +1 penalty, cost multiplied by depth |
| E11013 | Expression Complexity | Max: 15 points | Nested coalescing operators (??, ?:) |
| E11022 | Class Should Be Component | ≥4 service fields, 0 data | Pure service coordinator should use defines component |
| E11023 | Missing 'by' Delegation | ≥70% override, ≥5 methods | Manual forwarding should use by delegation |
| E11024 | Hybrid Class-Component | 1+ by, ≥3 services |
Mixed responsibilities should be split |
| E11025 | Excessive Mixed Responsibilities | ≥3 services AND ≥3 data | Blob/God Class - split into component + data class |
What adds complexity: Each if, switch case, while,
for loop, Boolean operator (and, or), exception handler,
and stream pipeline stage adds to the complexity score.
Cognitive Complexity (E11021): Based on SonarQube's Cognitive Complexity metric,
adapted for EK9. Formula: (base_cost + guard_penalty) × nesting_depth.
Guard expressions (<-) pack declaration + conditional check into one line,
so they add +1 cognitive penalty. A 6-level guard chain exceeds the threshold (42 > 35)
while 6 simple ifs pass (21 < 35). This catches code that's "hard to understand"
before E11011's structural limit at depth 6.
4.2 Cohesion Metrics (LCOM4)
Cohesion measures how well the methods of a class work together. EK9 uses LCOM4 (Lack of Cohesion of Methods, version 4) from Hitz & Montazeri (1995).
| Error Code | Construct | Threshold | What It Means |
|---|---|---|---|
| E11014 | Class | Max LCOM4: 8 | Class has too many unrelated method groups |
| E11014 | Component | Max LCOM4: 10 | Component coordinates too many concerns |
Plain English: If your class has methods that don't share any fields with each other, it's doing too many unrelated things. Split it into focused classes.
Exemptions: Traits, Records, and Functions are exempt from cohesion checks by design — traits have abstract methods (no field access), records focus on data operators, and functions have a single body.
4.3 Coupling Metrics (Efferent Coupling)
Coupling measures how many external types a construct depends on. High coupling creates fragile code that breaks when dependencies change. EK9 tracks efferent coupling (Ce) — the count of external types your construct references.
| Error Code | Construct | Threshold | What It Means |
|---|---|---|---|
| E11015 | Class | Max Ce: 12 | Class depends on too many external types |
| E11015 | Component/Service | Max Ce: 15 | Entry point coordinates too many dependencies |
| E11015 | Trait/Record/Function | Max Ce: 8 | Simpler constructs should have fewer dependencies |
What's tracked: Field types, method parameter types, return types, super classes, implemented traits, and captured variables in dynamic constructs.
What's excluded: Built-in types from org.ek9.lang (Integer, String, List, etc.)
are not counted toward coupling — they're part of the language, not external dependencies.
4.4 Module-Level Quality Metrics
Beyond construct-level metrics, EK9 also enforces quality at the module level. These metrics operate across all source files in a module and catch architectural issues that span multiple files.
Module Coupling (E11016)
Counts the number of distinct external modules a module depends on. High module coupling indicates a module that depends on too many other modules, making it fragile and difficult to maintain in isolation.
| Error Code | Metric | Threshold | What It Means |
|---|---|---|---|
| E11016 | External Module Count | Max: 10 | Module depends on too many other modules |
What's tracked: All type references to constructs defined in other modules (excluding
built-in org.ek9.* modules). Includes field types, method parameters, return types,
super classes, and implemented traits.
What's excluded: References to org.ek9.lang built-in types are not counted
as external dependencies — they're part of the language runtime.
Module Cohesion (E11017)
Measures how well the constructs within a module relate to each other. Uses connected component analysis (similar to LCOM4) to detect modules with many unrelated construct groups that should potentially be split into separate modules.
| Error Code | Metric | Threshold | What It Means |
|---|---|---|---|
| E11017 | Disconnected Groups | Max: 30 groups AND >88% | Module has too many unrelated construct groups |
How it works: Constructs that reference each other (through field types, method parameters, inheritance, etc.) are connected. Disconnected groups are clusters of constructs with no references between them. Many disconnected groups suggest the module could be split.
Exemptions: Small modules (<60 constructs) are exempt — module cohesion is only meaningful for large modules where fragmentation is a real concern.
Thresholds: Uses a hybrid approach requiring BOTH absolute (>30 groups) AND relative (>88% disconnected) thresholds to be exceeded. This allows legitimate utility modules with many independent constructs while catching truly fragmented modules.
4.5 Inheritance Depth Limits
Deep inheritance hierarchies make code hard to understand because you must trace through
many levels to understand behavior. EK9 limits inheritance depth and encourages
delegation via the by keyword as an alternative.
| Error Code | Construct | Max Depth | Rationale |
|---|---|---|---|
| E11019 | Class | 4 | Standard aggregate |
| E11019 | Trait | 4 | Interface composition chains |
| E11019 | Component | 4 | Entry point specialization |
| E11019 | Function | 3 | Function inheritance is rare |
| E11019 | Record | 2 | Data-focused, minimal behavior |
Better Alternative: Instead of deep inheritance, use composition with the by
keyword to delegate behavior:
#!ek9
defines module delegation.example
defines trait
Printable
print() as abstract
defines class
ConsolePrinter is Printable
override print()
stdout.println("Console output")
// Instead of: class MyClass extends DeepHierarchy
// Use delegation:
MyClass with trait of Printable by printer
printer as Printable: ConsolePrinter()
4.6 Combined Complexity and Size (Novel)
EK9 introduces a novel combined metric that catches the "both moderately high" scenario that passes individual complexity (E11010) and statement count (E11012) limits but represents a maintainability risk.
Research Basis: NASA Software Assurance Technology Center studies explicitly found that modules with both high complexity AND large size have the lowest reliability. Individual limits miss this critical combination.
Formula
combined = (complexity / maxComplexity) × (statements / maxStatements) threshold = 0.50
This creates a curved boundary — you can be high in one dimension if you're low in the other:
| Complexity | % | Statements | % | Combined | Result |
|---|---|---|---|---|---|
| 31/45 | 69% | 107/150 | 71% | 0.49 | ✓ passes |
| 32/45 | 71% | 108/150 | 72% | 0.51 | ✗ E11020 |
| 40/45 | 89% | 80/150 | 53% | 0.47 | ✓ passes |
| 45/45 | 100% | 75/150 | 50% | 0.50 | ✓ boundary |
Why This Approach is Novel
- SonarQube: Uses AND logic (independent conditions) — both must fail
- Microsoft MI: Uses arbitrary coefficients from 1990s research
- EK9: Product-based threshold directly implements what NASA research shows matters
Target: Both metrics below 70% of their limits achieves combined ≤0.49. The product formula naturally encourages balanced reduction — reducing one metric by 15% AND the other by 15% is more effective than reducing one metric by 30% alone.
Purity and Quality Integration
EK9's pure keyword isn't just about functional programming correctness —
it enables more accurate quality metrics and better optimization.
How Purity Enables Quality
- Eliminates hidden coupling: Pure functions cannot depend on mutable global state, so all dependencies are explicit in the signature
- Accurate coupling measurement: Since there are no hidden dependencies, Ce (efferent coupling) accurately reflects true coupling
- Enables parallelization: Pure functions can be called in parallel without synchronization, enabling the compiler to optimize for multi-core processors
- Simplifies testing: Pure functions are deterministic — same inputs always produce same outputs, making them trivially testable
EK9 implements a three-tier purity model:
- Compile-time enforcement: The
purekeyword is checked by the compiler - Pragmatic I/O: I/O operations are tracked but allowed in non-pure contexts
- Controlled mutation: Mutation is explicit and contained, not hidden
Operator Semantic Constraints
Beyond structural metrics, EK9 enforces semantic constraints on operators at compile-time. This ensures operators behave correctly and consistently across all types. See Operators for the full operator reference.
Return Type Constraints
Each operator category has strict return type requirements:
| Operator Category | Operators | Required Return Type | Error Code |
|---|---|---|---|
| Comparison | <, <=, >, >=, ==, <> |
Boolean | E07520 |
| Comparator | <=>, <~> |
Integer | E07550 |
| Is-Set | ? |
Boolean | E07520 |
| Hash Code | #? |
Integer | E07550 |
| String Conversion | $ |
String | E07570 |
| JSON Conversion | $$ |
JSON | E07580 |
| Complement/Absolute | ~, abs |
Same as construct type | E07410 |
| Promotion | #^ |
Different from construct type | E07420 |
Purity Constraints
Operators are divided into pure (no side effects) and mutating categories:
- Must be pure (E07500): All comparison operators, arithmetic operators, conversions, and queries
- Cannot be pure (E07510): Mutating operators like
:=:(copy),++,--,+=,-=
Defaulting Requirements
When using the default keyword to auto-generate comparison operators, the compiler
validates that all required underlying operators exist:
- Comparison operators (
<,<=,>,>=,==,<>) require the<=>comparator on the type and all properties (E07180, E07200) - Hash code (
#?) requires hash code on all properties - String conversion (
$) requires string conversion on all properties
This ensures that defaulted operators have correct semantics — a comparison can only be auto-generated if the type knows how to compare all its parts.
Parameter Safety
EK9 enforces parameter immutability at compile-time, preventing a common source of bugs where function parameters are accidentally modified.
Parameter Count Limits
Functions and methods have a hard limit of 20 parameters. Exceeding this triggers E11010 (Excessive Complexity). Additionally, parameter count contributes to complexity scoring:
- 3+ parameters: adds 1 complexity point
- 5+ parameters: adds 2 complexity points
- 20+ parameters: compilation error
This encourages grouping related parameters into records, improving API design.
No Parameter Reassignment
Function and method parameters cannot be reassigned within the function body (E08110). This eliminates bugs where the original parameter value is lost:
#!ek9 @Error: FULL_RESOLUTION: NO_INCOMING_ARGUMENT_REASSIGNMENT processData() -> data as Integer <- result as Integer? data := data + 1 //Error: cannot reassign parameter result: data * 2
#!ek9 processData() -> data as Integer <- result as Integer? adjusted <- data + 1 //Create new variable instead result: adjusted * 2
Why This Matters
- Prevents shadowing bugs: Original parameter value is always available
- Improves readability: Parameters are guaranteed to hold their original values
- Enables optimization: Compiler knows parameters won't change, enabling better code generation
- Matches functional style: Aligns with EK9's support for pure functions
Impact on AI-Assisted Development
As AI coding assistants become more prevalent, EK9's compile-time quality enforcement provides unique benefits for human-AI collaboration.
The AI Code Quality Problem
AI coding assistants (like GitHub Copilot, Claude, and others) can generate code quickly, but often produce code with quality issues:
- Excessive complexity: AI tends to generate long functions with many conditions
- High coupling: AI may add unnecessary dependencies to solve problems quickly
- Duplication: AI doesn't always recognize when code should be refactored into shared utilities
- Deep hierarchies: AI may follow inheritance patterns from training data
How EK9 Helps
EK9's compiler provides immediate feedback that helps both humans and AI improve:
- Constraint-based learning: AI learns from compiler errors, improving suggestions over time
- Guaranteed maintainability: AI-generated code that compiles is guaranteed to meet quality standards
- Objective standards: No debate about code quality — the compiler is the arbiter
- Verbose errors: The
-veflag provides detailed explanations that help AI understand issues
Enterprise Benefits
For enterprise teams adopting AI coding assistants, EK9 provides crucial safeguards:
- Zero configuration: No need to set up and maintain external quality tools
- Consistent standards: Same quality bar across all projects and teams
- Faster onboarding: New developers (and AI) learn good design from compiler feedback
- Technical debt prevention: Cannot accumulate quality debt through AI-generated code
Implementation Roadmap
EK9's quality enforcement is being implemented in phases:
Completed (2024-2025)
- Phase 1: Complexity Limits (E11010, E11011, E11013, E11021) — functions, nesting, expressions, cognitive complexity
- Phase 2A: Cohesion Metrics (E11014) — LCOM4 for classes and components
- Phase 2B: Coupling Metrics (E11015) — efferent coupling for all construct types
- Phase 2C: Inheritance Depth (E11019) — all construct types with construct-specific thresholds
- Phase 2D: Size Limits NCSS (E11012) — statement counts for functions (150), methods (100), operators (50)
- Phase 2E: Combined Complexity×Size (E11020) — novel product-based threshold catching "both moderately high" scenario (research: NASA SATC)
- Phase 2F: Construct Misuse Detection (E11022-E11025) — detects class/component/delegation misuse patterns including Blob/God Class (research: Martin SRP, Brown AntiPatterns, Marinescu IEEE ICSM 2004)
- Phase 2G: Module-Level Metrics (E11016, E11017) — module coupling (>10 external modules) and module cohesion (disconnected construct groups) for large modules
Planned (2026)
- Phase 3A: Duplicate Code Detection — IR-based similarity analysis (>70% = error)
- Phase 3B: Unused Capture Detection (E11018) — detect captured variables in dynamic functions that are never used, indicating potential bugs or unnecessary memory retention
Future (2027+)
- Phase 4: Security Pattern Enforcement — SQL injection, path traversal, hardcoded credentials
- Phase 5: Style Enforcement — consistent formatting as part of compilation
Deferred (Requires IR Generation Completion)
- Phase 2D: Afferent Coupling (Ca) — "who depends on this construct" analysis
- Phase 2E: Module Instability — Ca/(Ca+Ce) ratio calculations
Summary
EK9's compile-time quality enforcement represents a fundamental shift in how programming languages approach code quality. By making quality mandatory rather than optional, EK9 eliminates technical debt at its source.
The EK9 Guarantee: If your code compiles, it has met rigorous quality standards for complexity, cohesion, coupling, and inheritance depth. There is no gray area, no accumulated warnings, and no technical debt.
Next Steps
- See the Error Index for detailed documentation of all quality error codes (E11010-E11019)
- Learn about composition and delegation as an alternative to deep inheritance
- Explore packaging to understand how EK9 manages dependencies
- Review command line options including the
-veverbose error flag