Health Scores¶
Qualimetrix computes six health scores for every class, namespace, and project — each ranging from 0 (worst) to 100 (best). Health scores distill dozens of raw metrics into a quick quality overview, helping you spot problems without reading individual metric values.
Dimensions¶
| Dimension | What it measures | Key input metrics | Default thresholds (warning / error) |
|---|---|---|---|
health.complexity |
Method and class complexity | CCN (avg, max, p95), Cognitive Complexity (avg, max, p95) | 50 / 25 |
health.cohesion |
How well class methods relate to each other | TCC, LCOM4, method count | 50 / 25 |
health.coupling |
Dependencies between classes and namespaces | CBO, Distance from Main Sequence, efferent coupling | 50 / 25 |
health.typing |
Type declaration coverage | Parameter, return, and property type coverage | 80 / 50 |
health.maintainability |
Ease of safe modification | Maintainability Index (avg, p5, min) | 50 / 25 |
health.overall |
Weighted average of all dimensions | All of the above | 50 / 30 |
Score Labels¶
Every health score is assigned a human-readable label based on the score value relative to the warning (W) and error (E) thresholds:
- Excellent: score > W + (100 - W) x 0.6
- Good: score > W + (100 - W) x 0.3
- Fair: score > W
- Poor: score > E
- Critical: score <= E
For the most common defaults (W=50, E=25):
| Label | Score range |
|---|---|
| Excellent | > 80 |
| Good | 65 -- 80 |
| Fair | 50 -- 65 |
| Poor | 25 -- 50 |
| Critical | <= 25 |
Note
health.typing uses different thresholds (W=80, E=50), so its label boundaries shift accordingly: Excellent > 92, Good > 86, Fair > 80, Poor > 50, Critical <= 50.
How Scores Work¶
All health scores start from 100 and subtract penalties for metrics that exceed healthy thresholds. Each dimension has level-specific formulas — class, namespace, and project levels use different inputs because different aggregation statistics are available. Namespace and project formulas use aggregated statistics (.avg, .p95, .max, .min, .p5) while class formulas use raw per-class values.
Formulas are written in Symfony Expression Language syntax.
Complexity¶
Penalizes high average CCN and cognitive complexity, plus square-root-scaled penalties for outlier methods (max values at class level, p95 at namespace level). Well-structured code with simple methods scores near 100.
Interface methods are included in aggregation
Interface methods have minimal complexity (CCN=1, cognitive=0, NPath=1) and are included in namespace-level .avg and .p95 calculations. Projects with many interfaces may see lower average complexity than expected. This is by design — interfaces are part of the codebase — but means adding interfaces can slightly improve complexity scores without changing actual logic.
Cohesion¶
Blends TCC (Tight Class Cohesion) and LCOM4. TCC is square-root-scaled to reward incremental improvement. Classes with few methods (< 6) get a lenient TCC default. Pure methods (no property access) are accounted for to avoid false penalties.
Coupling¶
Uses hyperbolic decay (K / (K + penalty)) for smooth scoring. At class level, blends package-level and raw efferent coupling. At namespace level, combines Distance from Main Sequence, average CBO, and outlier CBO (p95, max).
Typing¶
At class level, directly maps type coverage percentage. At namespace and project level, computes the ratio from raw typed/total counters to avoid averaging bias.
Maintainability¶
Three-term penalty on MI average (base quality), MI 5th percentile (main differentiator), and MI minimum (extreme outliers). The multi-term approach produces good discrimination across projects — from well-maintained libraries (score ~95) to complex frameworks (score ~48).
Overall¶
Weighted average of the other five dimensions. At class level, maintainability is excluded (its signal is already captured by complexity and cohesion). Weights:
- Class: complexity 35%, cohesion 25%, coupling 25%, typing 15%
- Namespace / Project: complexity 30%, cohesion 20%, coupling 20%, typing 10%, maintainability 20%
Reading Health Scores¶
Health scores appear in several output formats:
- Summary format (
--format=summary, default) — progress bars with color coding and labels - JSON format (
--format=json) —healthScoresarray in the output object - Health format (
--format=health) — text table of health dimensions with scores, status, and decomposition - HTML format (
--format=html) — interactive treemap colored by selected health dimension
See Output Formats for details.
Configuration¶
Customizing Thresholds¶
# qmx.yaml
computed_metrics:
health.complexity:
warning: 60 # Stricter than default 50
error: 30 # Stricter than default 25
Disabling a Dimension¶
Or exclude from display only (scores still computed):
Overriding Formulas¶
computed_metrics:
health.maintainability:
# Same formula for all levels
formula: "clamp(mi__avg, 0, 100)"
computed_metrics:
health.maintainability:
# Different formulas per level
formulas:
class: "clamp(mi__avg, 0, 100)"
namespace: "clamp(mi__avg * 0.7 + mi__p5 * 0.3, 0, 100)"
project: "clamp(mi__avg * 0.7 + mi__p5 * 0.3, 0, 100)"
Custom Computed Metrics¶
computed_metrics:
computed.code-density:
formula: "clamp((lloc ?? 0) / max(loc ?? 1, 1) * 100, 0, 100)"
description: "Ratio of logical to physical lines (higher = denser code)"
levels: [class, namespace, project]
warning: 80
error: 90
inverted: false # Higher values trigger violations
Metric naming
User-defined metrics can use any name except the reserved health.* prefix. The recommended convention is computed.*.
Available Variables¶
Metric names use double underscores in formulas (dots are not allowed in Expression Language identifiers):
| Metric | Variable in formula | Available at |
|---|---|---|
ccn.avg |
ccn__avg |
class, namespace, project |
ccn.max |
ccn__max |
class, namespace, project |
ccn.sum |
ccn__sum |
namespace, project |
ccn.p95 |
ccn__p95 |
namespace, project |
cognitive.avg |
cognitive__avg |
class, namespace, project |
cognitive.max |
cognitive__max |
class, namespace, project |
cognitive.sum |
cognitive__sum |
namespace, project |
cognitive.p95 |
cognitive__p95 |
namespace, project |
tcc |
tcc |
class |
tcc.avg |
tcc__avg |
namespace, project |
lcom |
lcom |
class |
lcom.avg |
lcom__avg |
namespace, project |
cbo.avg |
cbo__avg |
namespace, project |
cbo.max |
cbo__max |
namespace, project |
cbo.p95 |
cbo__p95 |
namespace, project |
ce |
ce |
class |
ce_packages |
ce_packages |
class |
mi.avg |
mi__avg |
class, namespace, project |
mi.min |
mi__min |
class, namespace, project |
mi.p5 |
mi__p5 |
namespace, project |
distance |
distance |
namespace |
distance.avg |
distance__avg |
project |
typeCoverage.pct |
typeCoverage__pct |
class |
methodCount |
methodCount |
class |
symbolMethodCount |
symbolMethodCount |
namespace, project |
pureMethodCount_cohesion |
pureMethodCount_cohesion |
class |
health.complexity |
health__complexity |
class, namespace, project |
Common aggregation suffixes: __avg, __min, __max, __sum, __p5, __p95.
This is not an exhaustive list — any metric collected by Qualimetrix can be referenced in formulas. Use bin/qmx check src/ --format=metrics-json to see all available metrics for your project.
Unknown metric references
If a formula references a metric that does not exist (e.g., a typo like ccn__abg instead of ccn__avg), Qualimetrix will report a clear error instead of silently returning zero. Always use the ?? operator to provide a default for metrics that may legitimately be absent: (ccn__avg ?? 0).
Available Functions¶
| Function | Description |
|---|---|
min(a, b) |
Minimum of two values |
max(a, b) |
Maximum of two values |
abs(x) |
Absolute value |
sqrt(x) |
Square root |
log(x) |
Natural logarithm |
log10(x) |
Base-10 logarithm |
clamp(value, min, max) |
Constrain value to [min, max] range |
?? |
Null coalescing (default value if metric is missing) |
** |
Exponentiation |
Always use null coalescing
Metrics may be missing for some symbols (e.g., a class with no methods has no ccn). Always provide defaults with ??: (ccn__avg ?? 1) instead of ccn__avg.