Skip to content
Arxo Arxo

Package Metrics

package_metrics measures package architecture quality using Martin-style stability metrics plus package cohesion/cycle diagnostics.
This page documents the current v2 key contract (package_metrics.*).

  • Module-level coupling and stability (Ca, Ce, weighted instability)
  • Package-level cohesion and dependency structure
  • Stable-to-unstable dependency anti-patterns (module and package level)
  • Package cycles and tangle
  • Lakos dependency accumulation (CCD/ACD/NCCD)
  • Martin outlier zones (zone of pain, zone of uselessness)
  • Optional API surface pressure (when call graph is available)
KeyMeaning
package_metrics.module.countNumber of analyzed modules
package_metrics.package.countNumber of discovered packages
package_metrics.module.instability.meanMean module instability
package_metrics.module.instability.maxMax module instability
package_metrics.module.instability.minMin module instability
package_metrics.module.instability.medianMedian module instability
package_metrics.module.instability.std_devStandard deviation of module instability
package_metrics.module.ca.meanMean afferent coupling (Ca)
package_metrics.module.ce.meanMean efferent coupling (Ce)
package_metrics.module.ca_weighted.meanMean weighted Ca
package_metrics.module.ce_weighted.meanMean weighted Ce
package_metrics.module.abstractness.meanMean abstractness (A)
package_metrics.module.distance.meanMean distance from main sequence (D)
package_metrics.module.distance.maxMax distance from main sequence

Dependency risks, cohesion, cycles, Lakos, zones

Section titled “Dependency risks, cohesion, cycles, Lakos, zones”
KeyMeaning
package_metrics.unstable_dependency.module.countStable module -> unstable module dependencies
package_metrics.unstable_dependency.package.countStable package -> unstable package dependencies
package_metrics.package.cohesion.relational.meanMean relational cohesion across packages
package_metrics.package.cohesion.relational.minLowest package cohesion
package_metrics.package.cohesion.relational.low_countPackages below configured cohesion threshold
package_metrics.package.cycle.component_countNumber of cyclic package components
package_metrics.package.cycle.member_countTotal packages participating in cycles
package_metrics.package.cycle.largest_component_sizeSize of largest package cycle
package_metrics.package.tangle_indexFraction of packages in cycles
package_metrics.package.lakos.ccdCumulative component dependency (Lakos CCD)
package_metrics.package.lakos.acdAverage component dependency (Lakos ACD)
package_metrics.package.lakos.nccdNormalized cumulative component dependency (Lakos NCCD)
package_metrics.zone.pain.countStable + concrete modules
package_metrics.zone.uselessness.countUnstable + abstract modules
KeyMeaning
package_metrics.api_surface_pressure.meanMean module boundary-call pressure
package_metrics.api_surface_pressure.maxMax module boundary-call pressure
package_metrics.api_surface_pressure.systemSystem-wide boundary-call ratio

Use table entries for detailed triage (instead of legacy details.* fields):

  • package_metrics.module.table
  • package_metrics.package.table
  • package_metrics.unstable_dependency.module.table
  • package_metrics.unstable_dependency.package.table
  • package_metrics.package.cycle.components.table
  • package_metrics.zone.table
  • package_metrics.layer.table (only when layer_order is configured)
  • package_metrics.module.table: module, path, package, layer, ca, ce, ca_weighted, ce_weighted, instability, abstractness, distance, zone
  • package_metrics.package.table: package, module_count, ca, ce, instability, relational_cohesion, internal_edge_count
  • package_metrics.unstable_dependency.module.table: source_module, source_instability, target_module, target_instability
  • package_metrics.zone.table: module, package, zone, instability, abstractness, distance

This metric emits deterministic findings with rule IDs:

  • arxo/package-metrics/stable-to-unstable-module
  • arxo/package-metrics/stable-to-unstable-package
  • arxo/package-metrics/package-cycle
  • arxo/package-metrics/zone-of-pain
  • arxo/package-metrics/zone-of-uselessness

These are included in MetricResult.findings and surfaced in SARIF/JSON reports.

  • Module instability: I = Ce_weighted / (Ca_weighted + Ce_weighted)
  • Module distance from main sequence: D = |A + I - 1|
  • Package instability: I = Ce / (Ca + Ce)
  • Relational cohesion: internal_edge_count / (n * (n - 1)) for n > 1, else 1.0
  • Tangle index: cyclic_member_count / total_packages
  • Lakos NCCD: (CCD - P) / (P * (P - 1)) for P > 1, else 0.0

Fallbacks:

  • If coupling denominator is zero, instability is set to 0.5
  • Requires import graph and workspace detection.
  • Uses type graph when available to compute abstractness (A); if unavailable, abstractness falls back to 0.
  • Uses call graph when available for package_metrics.api_surface_pressure.*; otherwise those values are 0.
  • Runtime is linear for most passes (module/package aggregation), plus transitive reachability work for Lakos metrics (CCD/ACD/NCCD) which can dominate on dense package graphs.
metrics:
- id: package_metrics
enabled: true
config:
stable_threshold: 0.30
unstable_threshold: 0.70
cohesion_low_threshold: 0.20
zone_pain_abstractness_max: 0.30
zone_useless_abstractness_min: 0.70
layer_order: ["app", "domain", "infra"] # optional
OptionDefaultPurpose
stable_threshold0.30Max instability considered stable
unstable_threshold0.70Min instability considered unstable
cohesion_low_threshold0.20Packages below this are marked low cohesion
zone_pain_abstractness_max0.30Zone of pain: stable && A <= threshold
zone_useless_abstractness_min0.70Zone of uselessness: unstable && A >= threshold
layer_order[]Enables package_metrics.layer.table aggregation
metrics:
- id: package_metrics
enabled: true
policy:
invariants:
- metric: package_metrics.module.instability.mean
op: ">="
value: 0.30
- metric: package_metrics.module.instability.mean
op: "<="
value: 0.60
- metric: package_metrics.unstable_dependency.module.count
op: "=="
value: 0
- metric: package_metrics.package.cycle.component_count
op: "=="
value: 0
policy:
invariants:
- metric: package_metrics.unstable_dependency.module.count
op: "<="
value: 3
- metric: package_metrics.module.instability.max
op: "<="
value: 0.95
- metric: package_metrics.package.cohesion.relational.low_count
op: "<="
value: 5
{
"id": "package_metrics",
"data": [
{ "key": "package_metrics.module.instability.mean", "value": 0.42 },
{ "key": "package_metrics.unstable_dependency.module.count", "value": 1 },
{ "key": "package_metrics.package.cycle.component_count", "value": 0 },
{
"key": "package_metrics.unstable_dependency.module.table",
"columns": ["source_module", "source_instability", "target_module", "target_instability"],
"rows": [["src/core/helpers.ts", 0.18, "src/features/cart/service.ts", 0.87]]
}
],
"findings": [
{
"rule_id": "arxo/package-metrics/stable-to-unstable-module",
"title": "Stable module depends on unstable module: src/core/helpers.ts -> src/features/cart/service.ts"
}
]
}

package_metrics v2 removed legacy top-level keys such as package_metrics.instability_avg.

Legacy keyv2 replacement
package_metrics.instability_avgpackage_metrics.module.instability.mean
package_metrics.instability_maxpackage_metrics.module.instability.max
package_metrics.instability_minpackage_metrics.module.instability.min
package_metrics.instability_medianpackage_metrics.module.instability.median
package_metrics.unstable_dependency_countpackage_metrics.unstable_dependency.module.count and package_metrics.unstable_dependency.package.count
package_metrics.ca_avgpackage_metrics.module.ca.mean
package_metrics.ce_avgpackage_metrics.module.ce.mean

Removed with no direct single-key replacement:

  • package_metrics.stable_count
  • package_metrics.balanced_count
  • package_metrics.unstable_count

Use package_metrics.module.table and your configured thresholds to derive those counts.