The report has three values per class or method, but none says where a change most likely breaks something unnoticed.
Mutation-CRAP per method misses a large class whose many simple methods are weakly tested.
In werkcrappit, ClassReferences has no method above 6.8, but 43 % of its mutants survive.
Non-Goals
A limit for the change risk: the gate keeps judging the single values (@MC-CR-005).
Any change of Mutation-CRAP, LCOM4 or Ce.
The Scenarios
The scenarios were agreed one by one on 2026-10-04, in the new feature file 09-change-risk.feature and in 07-codecharta.feature.
Name, formula and constants were decided before: "Change risk", Mutation-CRAP over the whole class as the base (option C of three), fixed k_L = 2 and k_C = 20, a band from 30 to 100.
@MC-CR-001 change risk is Mutation-CRAP over the whole class: 20 methods of complexity 1 with half their mutants killed give 70, though no method exceeds 2.
@MC-CR-002 LCOM4 and Ce raise it, × (1 + (LCOM4 − 1)/2) × (1 + Ce/20), as an exact fraction.
@MC-CR-003 only scored methods count, nested classes belong to their top-level class; no scored method, no change risk; no LCOM4 counts as 1.
@MC-CR-004 an INCONCLUSIVE method or one without complexity voids the change risk of its class, with the reason method-not-scored:….
@MC-CR-005 the gate does not judge the change risk, and the methods' Mutation-CRAP stays as it was.
@MC-CR-006mutation-crap.md lists the ten classes with the highest change risk, with what makes them risky.
@MC-CC-035 the city has the metric change_risk, a fourth scenario change-risk.ccscenario and a link coloured by it.
ChangeRisk in the metric core computes the value as a reduced fraction from the summed complexity and mutants, LCOM4 and Ce; its base is MutationCrap.of.
ChangeRisks joins the methods to their top-level classes; ClassDesign carries the result as ClassRisk. ReportData does the join, so JSON, Markdown and console read the same values, and the listener's coupling stays at its ratchet of 21.
mutation-crap.json has schema version 3; the gate and the export read version 3.
mutation-crap.md has a section "Change Risk"; the city a metric change_risk, a scenario and a link, yellow from 30 and red from 100.
Version 0.22.0.
Additional Changes
FeatureFilesTest now also checks the IDs MC-CK-… and MC-CR-… against the acceptance map; it knew only MC-… and MC-CC-….
The acceptance map is column-aligned, and its note on test locations moved below the table, where it no longer splits it.
Follow-up PRs
werkcrappit-pit uses the Gradle plugin 0.22.0: until then the nightly mutation build fails, as its gate reads schema version 2.
## Related Links
- [ADR 0008](../adr/0008-change-risk.md), the formula and its alternatives
- [RFC 0002](../rfcs/0002-cohesion-and-coupling.md), which brought LCOM4 and Ce
## The Problem
The report has three values per class or method, but none says where a change most likely breaks something unnoticed.
Mutation-CRAP per method misses a large class whose many simple methods are weakly tested.
In werkcrappit, `ClassReferences` has no method above 6.8, but 43 % of its mutants survive.
## Non-Goals
- A limit for the change risk: the gate keeps judging the single values (`@MC-CR-005`).
- Any change of Mutation-CRAP, LCOM4 or Ce.
## The Scenarios
The scenarios were agreed one by one on 2026-10-04, in the new feature file `09-change-risk.feature` and in `07-codecharta.feature`.
Name, formula and constants were decided before: "Change risk", Mutation-CRAP over the whole class as the base (option C of three), fixed `k_L = 2` and `k_C = 20`, a band from 30 to 100.
- `@MC-CR-001` change risk is Mutation-CRAP over the whole class: 20 methods of complexity 1 with half their mutants killed give 70, though no method exceeds 2.
- `@MC-CR-002` LCOM4 and Ce raise it, `× (1 + (LCOM4 − 1)/2) × (1 + Ce/20)`, as an exact fraction.
- `@MC-CR-003` only scored methods count, nested classes belong to their top-level class; no scored method, no change risk; no LCOM4 counts as 1.
- `@MC-CR-004` an `INCONCLUSIVE` method or one without complexity voids the change risk of its class, with the reason `method-not-scored:…`.
- `@MC-CR-005` the gate does not judge the change risk, and the methods' Mutation-CRAP stays as it was.
- `@MC-CR-006` `mutation-crap.md` lists the ten classes with the highest change risk, with what makes them risky.
- `@MC-CC-035` the city has the metric `change_risk`, a fourth scenario `change-risk.ccscenario` and a link coloured by it.
##### Verified by
- [ChangeRiskTest](../../werkcrappit-pit/src/test/java/de/javagil/werkcrappit/pit/core/ChangeRiskTest.java)
- [ChangeRisksTest](../../werkcrappit-pit/src/test/java/de/javagil/werkcrappit/pit/design/ChangeRisksTest.java)
- [JsonReportTest](../../werkcrappit-pit/src/test/java/de/javagil/werkcrappit/pit/report/JsonReportTest.java) `theClassEntryCarriesTheChangeRisk`
- [MarkdownReportTest](../../werkcrappit-pit/src/test/java/de/javagil/werkcrappit/pit/report/MarkdownReportTest.java) `theTenClassesWithTheHighestChangeRisk`
- [GateTest](../../werkcrappit-pit/src/test/java/de/javagil/werkcrappit/pit/gate/GateTest.java) `changeRiskIsNotJudged`
- [ReportInputTest](../../werkcrappit-pit/src/test/java/de/javagil/werkcrappit/pit/codecharta/ReportInputTest.java) `theChangeRiskOfTheReportIsReadBackExactly`
- [CodeCityTest](../../werkcrappit-pit/src/test/java/de/javagil/werkcrappit/pit/codecharta/CodeCityTest.java) `theCityShowsTheChangeRisk`, [CodeCityScenariosTest](../../werkcrappit-pit/src/test/java/de/javagil/werkcrappit/pit/codecharta/CodeCityScenariosTest.java) `changeRiskIsTheFourthScenario`, [CodeCityPageTest](../../werkcrappit-pit/src/test/java/de/javagil/werkcrappit/pit/codecharta/CodeCityPageTest.java) `thePageOpensTheCityByChangeRisk`
- [CodeChartaFunctionalTest](../../werkcrappit-gradle-plugin/src/functionalTest/java/de/javagil/werkcrappit/gradle/CodeChartaFunctionalTest.java) `aRealBuildWritesAValidCity`
## The Solution
- `ChangeRisk` in the metric core computes the value as a reduced fraction from the summed complexity and mutants, LCOM4 and Ce; its base is `MutationCrap.of`.
- `ChangeRisks` joins the methods to their top-level classes; `ClassDesign` carries the result as `ClassRisk`.
`ReportData` does the join, so JSON, Markdown and console read the same values, and the listener's coupling stays at its ratchet of 21.
- `mutation-crap.json` has schema version 3; the gate and the export read version 3.
- `mutation-crap.md` has a section "Change Risk"; the city a metric `change_risk`, a scenario and a link, yellow from 30 and red from 100.
- Version 0.22.0.
## Additional Changes
- `FeatureFilesTest` now also checks the IDs `MC-CK-…` and `MC-CR-…` against the acceptance map; it knew only `MC-…` and `MC-CC-…`.
- The acceptance map is column-aligned, and its note on test locations moved below the table, where it no longer splits it.
## Follow-up PRs
- werkcrappit-pit uses the Gradle plugin 0.22.0: until then the nightly mutation build fails, as its gate reads schema version 2.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Mutation-CRAP over all scored methods of a top-level class, times
(1 + (LCOM4 - 1)/2) and (1 + Ce/20), as an exact fraction (ADR 0008).
Report schema version 3, a Markdown section, and a fourth view in the
code city. The gate does not judge it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mi
merged commit 18248cb975 into main2026-10-04 16:51:32 +02:00
mi
deleted branch mihoe/change-risk2026-10-04 16:51:32 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Related Links
The Problem
The report has three values per class or method, but none says where a change most likely breaks something unnoticed.
Mutation-CRAP per method misses a large class whose many simple methods are weakly tested.
In werkcrappit,
ClassReferenceshas no method above 6.8, but 43 % of its mutants survive.Non-Goals
@MC-CR-005).The Scenarios
The scenarios were agreed one by one on 2026-10-04, in the new feature file
09-change-risk.featureand in07-codecharta.feature.Name, formula and constants were decided before: "Change risk", Mutation-CRAP over the whole class as the base (option C of three), fixed
k_L = 2andk_C = 20, a band from 30 to 100.@MC-CR-001change risk is Mutation-CRAP over the whole class: 20 methods of complexity 1 with half their mutants killed give 70, though no method exceeds 2.@MC-CR-002LCOM4 and Ce raise it,× (1 + (LCOM4 − 1)/2) × (1 + Ce/20), as an exact fraction.@MC-CR-003only scored methods count, nested classes belong to their top-level class; no scored method, no change risk; no LCOM4 counts as 1.@MC-CR-004anINCONCLUSIVEmethod or one without complexity voids the change risk of its class, with the reasonmethod-not-scored:….@MC-CR-005the gate does not judge the change risk, and the methods' Mutation-CRAP stays as it was.@MC-CR-006mutation-crap.mdlists the ten classes with the highest change risk, with what makes them risky.@MC-CC-035the city has the metricchange_risk, a fourth scenariochange-risk.ccscenarioand a link coloured by it.Verified by
theClassEntryCarriesTheChangeRisktheTenClassesWithTheHighestChangeRiskchangeRiskIsNotJudgedtheChangeRiskOfTheReportIsReadBackExactlytheCityShowsTheChangeRisk, CodeCityScenariosTestchangeRiskIsTheFourthScenario, CodeCityPageTestthePageOpensTheCityByChangeRiskaRealBuildWritesAValidCityThe Solution
ChangeRiskin the metric core computes the value as a reduced fraction from the summed complexity and mutants, LCOM4 and Ce; its base isMutationCrap.of.ChangeRisksjoins the methods to their top-level classes;ClassDesigncarries the result asClassRisk.ReportDatadoes the join, so JSON, Markdown and console read the same values, and the listener's coupling stays at its ratchet of 21.mutation-crap.jsonhas schema version 3; the gate and the export read version 3.mutation-crap.mdhas a section "Change Risk"; the city a metricchange_risk, a scenario and a link, yellow from 30 and red from 100.Additional Changes
FeatureFilesTestnow also checks the IDsMC-CK-…andMC-CR-…against the acceptance map; it knew onlyMC-…andMC-CC-….Follow-up PRs
🤖 Generated with Claude Code