Why does EK9 reject a module whose constructs are all unrelated?

← Code Quality · Ref: Q1337

EK9 measures how connected the constructs inside one module are. Two constructs are 'connected' when one references the other's type. A grab-bag module - String helpers next to maths helpers next to date helpers, none sharing any types - forms many disconnected groups. When a module exceeds ALL THREE thresholds at once (more than 88% disconnection AND more than 30 disconnected groups AND more than 60 constructs) EK9 raises E11017, because the module is really several modules glued together.

The fix is the Split Module refactoring: gather constructs that share types into focused modules (qa.text, qa.geometry, qa.time, ...), each with one clear purpose. Constructs that share no types probably do not belong together. The example below is one such cohesive module - every construct references the Point type, so its constructs form a single connected group and cohesion is high.

See Q314 for how EK9 measures cohesion and coupling. See Q311 for the full quality-check catalog.

Example

defines module qa.geometry.points

  defines record

    <?-
      Shared type that binds every construct in this module together.
      Because the function and the program both reference Point, the
      module forms a single connected group - high cohesion, no E11017.
    -?>
    Point
      x as Float: Float()
      y as Float: Float()

      Point() as pure
        ->
          initialX as Float
          initialY as Float
        this.x :=? initialX
        this.y :=? initialY

      operator $ as pure
        <- rtn as String: `(${x}, ${y})`

      default operator ?

  defines function

    //Operates on the shared Point type, keeping the module cohesive.
    distanceFromOrigin() as pure
      -> point as Point
      <- rtn as Float: Float()
      sumOfSquares <- point.x * point.x + point.y * point.y
      rtn: sumOfSquares.sqrt()

  defines program

    ModuleCohesionDemo()
      stdout <- Stdout()
      origin <- Point(3.0, 4.0)
      stdout.println(`Point ${origin} is ${distanceFromOrigin(origin)} from origin`)
Other ways to ask this
  • What triggers E11017 poor module cohesion?
  • Why must I split a grab-bag utilities module in EK9?
  • How does EK9 measure module construct disconnection?

Coming from another language?

Java: package contents are unconstrained; cohesion is only reported (LCOM, package-tangle) by SonarQube/JDepend as advisory metrics, never enforced. Kotlin/C#/Go: no module-cohesion enforcement - a 'utils' grab-bag compiles silently. EK9: low module cohesion is a hard compiler error (E11017) once the disconnection, group-count, and construct-count thresholds are all exceeded, forcing the Split Module refactoring.

Keywords: quality, disconnection, split, group, E11017, grab-bag, connected, utilities, cohesion, module