## Related Links - [RFC 0002](../rfcs/0002-cohesion-and-coupling.md) ## The Problem werkcrappit shows how complex and how well tested a method is, but not how well a class holds together or how strongly classes depend on each other. Adding both is larger than one pull request. ## The Solution - RFC 0002 proposes LCOM4 for cohesion and Ca/Ce for coupling per top-level class, beside Mutation-CRAP, never mixed into it, with optional limits `maxLcom4` and `maxEfferentCoupling`. The decisions taken on 2026-10-04 are in its section 2, the three implementation steps in section 7, and the two questions settled after the first draft in section 8. - Version 0.16.5; no change to the plugins. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #34
80 lines
5.2 KiB
Markdown
80 lines
5.2 KiB
Markdown
# RFC 0002: Cohesion and Coupling Beside Mutation-CRAP
|
|
|
|
- Status: proposed
|
|
- Date: 2026-10-04
|
|
- Author: Michael Hönnig
|
|
- Implementation: in three PRs (see section 7)
|
|
|
|
## 1. Goal
|
|
|
|
werkcrappit measures how complex a method is and how well its tests catch changes.
|
|
Two other main signs of design quality stay invisible: how well the parts of a class belong together, *cohesion*, and how strongly classes depend on each other, *coupling*.
|
|
The goal is "high cohesion, low coupling", measured per class, shown in the report and the code city, and with a gate of its own.
|
|
|
|
## 2. Decisions taken for this RFC
|
|
|
|
Each was a choice between options; the consequences are in the conversation of 2026-10-04.
|
|
|
|
- **Separate dimensions, no combined score.**
|
|
Mutation-CRAP stays as the specification defines it.
|
|
Cohesion and coupling are metrics of their own, each with its own limit.
|
|
A failing build always names the dimension; a combined number would hide whether to add tests, split a class or cut dependencies, and its weights would be guesses.
|
|
- **Unit: the top-level class**, as for the buildings and the edges of the code city (ADR 0007).
|
|
Nested classes and lambdas count for their top-level class.
|
|
- **Cohesion: LCOM4** (Hitz and Montazeri, 1995).
|
|
The instance methods of a class form a graph: two methods are connected when one calls the other or both use the same instance field.
|
|
LCOM4 is the number of connected parts; 1 is a cohesive class, more than 1 a class that could be split along its parts.
|
|
Constructors are left out, as they touch every field and would connect everything.
|
|
Methods the compiler or a processor like Lombok generates are left out, as JaCoCo filters them; a lambda body counts for its enclosing method.
|
|
Static methods and fields are left out.
|
|
A class without instance methods, after these rules, has no LCOM4 (`null`), never 0.
|
|
- **Coupling: Ca and Ce** between top-level classes of the analysed code, as the edges count them.
|
|
Ce (efferent) is the number of other classes a class depends on, Ca (afferent) the number of classes that depend on it.
|
|
Libraries and the JDK do not count.
|
|
- **Gate on cohesion and outgoing coupling only.**
|
|
`maxLcom4` and `maxEfferentCoupling` are new, optional limits.
|
|
Ca is shown, never judged: a domain type used everywhere is meant to have a high Ca.
|
|
Both limits are ratchets, as `maxMutationCrap` is in werkcrappit's own build.
|
|
|
|
## 3. Non-goals
|
|
|
|
- A change of the Mutation-CRAP formula or of its gate.
|
|
- Other cohesion measures (LCOM1 to LCOM3, TCC, LCC) and package metrics (instability, abstractness, distance from the main sequence); they may follow once the class values prove useful.
|
|
- Kotlin specifics: Kotlin's accessors are generated methods and fall out like Lombok's; anything beyond that waits for the specification's Kotlin step.
|
|
|
|
## 4. Where the values come from
|
|
|
|
The values come from the original class files, as the complexity does: no extra test run, no change to PIT.
|
|
The PIT listener already reads every analysed class file for the method inventory, and the CodeCharta export already reads their references for the edges.
|
|
Reading fields and calls per method is the same ASM pass with a little more bookkeeping.
|
|
|
|
Ca and Ce need every class of the analysed code, not only the mutated ones; the listener knows the code roots, as the export does.
|
|
|
|
## 5. The report
|
|
|
|
`mutation-crap.json` gets a section `classes`: per top-level class `lcom4`, `efferentCoupling`, `afferentCoupling`, and the method groups behind LCOM4, so a reader sees *what* would split.
|
|
As the report contract asks for any change of the shape (ADR 0004), `schemaVersion` goes from 1 to 2; the gate of the same release reads version 2.
|
|
`mutation-crap.md` gets a table of the classes with the highest LCOM4 and the highest Ce.
|
|
|
|
## 6. The gate and the code city
|
|
|
|
- `werkcrappit { maxLcom4 = 1; maxEfferentCoupling = 20 }`, both optional; unset, the dimension is reported, not judged.
|
|
`mutationCrapCheck` judges all configured dimensions and names each violation with its dimension.
|
|
- The city gets the metrics `lcom4`, `efferent_coupling` and `afferent_coupling`.
|
|
`lcom4` is `relative` (a district shows the median), the couplings `absolute`.
|
|
The report page links the city as before; a reader picks another colour metric in the viewer.
|
|
|
|
## 7. Steps
|
|
|
|
Each is one PR with green CI, a version of its own and Gherkin scenarios agreed one at a time, each with its reason (`… damit …`), in a new feature file `08-cohesion-coupling.feature`.
|
|
|
|
1. **Measure:** LCOM4, Ca and Ce in the report, schema version 2, a section in the Markdown report.
|
|
2. **Show:** the three metrics in the code city.
|
|
3. **Judge:** `maxLcom4` and `maxEfferentCoupling` in the gate and the Gradle DSL; werkcrappit's own build and the pilot hsadmin-ng get ratchets from their first reports.
|
|
|
|
## 8. Questions settled on 2026-10-04
|
|
|
|
- Getters and setters are methods like any other: a getter connects with every method using its field, as the published LCOM4 has it.
|
|
An entity with unrelated fields may thus show LCOM4 above 1; that is often true, and the limit is a judgement per project.
|
|
- A dependency on a nested class of another top-level class is a dependency on that top-level class, as for the edges; Ce counts top-level classes only.
|