How does EK9 measure cohesion and coupling?
← Code Quality · Ref: Q314
EK9 measures both cohesion and coupling at class and module level, using established software engineering metrics.
LACK OF COHESION - LCOM4 (E11014)
LCOM4 counts the number of connected components in a class. A connected component is a group of methods that share fields. A perfectly cohesive class has LCOM4 of 1 (all methods use overlapping fields). Higher values mean the class should be split. Thresholds: 8 for classes, 10 for components. Built-in types are excluded from this check.
EFFERENT COUPLING - Ce (E11015)
Efferent coupling counts how many other types a class depends on. High Ce means a class is tightly coupled to many other types and will break when any of them change. Thresholds: 8 for records, 12 for classes, 15 for components. Built-in types (String, Integer, List, etc.) are excluded because depending on standard library types is normal.
MODULE COUPLING (E11016)
Counts how many other modules a module depends on. High module coupling means changes ripple across module boundaries. Threshold: 10 external module dependencies.
MODULE COHESION (E11017)
Measures how related the contents of a module are. Low module cohesion suggests the module is a grab-bag of unrelated functionality and should be split.
CHIDAMBER AND KEMERER EVIDENCE
These metrics come from Chidamber and Kemerer (1994) research on object-oriented metrics. Their study of C++ projects showed that classes with high LCOM and high coupling had significantly more defects.
See Q311 for the full quality checks catalog. See Q312 for complexity metrics. See Q315 for inheritance depth.
See Q694 for named arguments pattern. See Q698 for excessive Boolean params.
Example
defines module qa.codequality.cohesion defines class <?- A cohesive class where all methods share fields. LCOM4 is 1 because every method uses the shared state. -?> TemperatureConverter celsiusReading as Float: Float() TemperatureConverter() -> initialCelsius as Float this.celsiusReading :=? initialCelsius celsius() as pure <- rtn as Float: celsiusReading toFahrenheit() as pure <- rtn as Float: Float() conversionFactor <- 9.0 / 5.0 freezingOffset <- 32.0 rtn: celsiusReading * conversionFactor + freezingOffset toKelvin() as pure <- rtn as Float: Float() kelvinOffset <- 273.15 rtn: celsiusReading + kelvinOffset override operator ? as pure <- rtn as Boolean: celsiusReading? operator $ as pure <- rtn as String: `${celsiusReading}C` defines program CohesionCouplingDemo() stdout <- Stdout() boilingPoint <- 100.0 reading <- TemperatureConverter(boilingPoint) stdout.println(`Celsius: ${reading.celsius()}`) stdout.println(`Fahrenheit: ${reading.toFahrenheit()}`) stdout.println(`Kelvin: ${reading.toKelvin()}`)
Common mistakes
E50001 — Renaming the variable means later references to 'reading' become unresolved, triggering E50001. See ek9 -h E50001 for details.
Incorrect:
readingXYZ <- TemperatureConverter(boilingPoint)
Correct:
reading <- TemperatureConverter(boilingPoint)
E50001 — Renaming the variable means later references to 'boilingPoint' become unresolved, triggering E50001. See ek9 -h E50001 for details.
Incorrect:
boilingPointXYZ <- 100.0
Correct:
boilingPoint <- 100.0
Other ways to ask this
- What is LCOM4 in EK9?
- How does EK9 detect tightly coupled modules?
- Does EK9 measure efferent coupling?
Coming from another language?
Java: SonarQube measures LCOM4 and coupling but as informational metrics, not enforcement. JDepend measures package coupling but is a separate tool. Rust: no cohesion or coupling metrics built-in or in clippy. Go: no cohesion or coupling metrics. Python: pylint has no coupling metrics. C++: CppDepend measures metrics but is a commercial separate tool. EK9: LCOM4, efferent coupling, and module coupling are mandatory compiler errors with fixed thresholds.
Keywords: E11015, E11014, kemerer, module, migrate, connected, chidamber, cohesion, LCOM4, quality, component, coupling, efferent, E11016, clean-code, metric