Skip to content
Arxo Arxo

Centrality Metrics

Centrality metrics measure the importance and influence of modules in your dependency graph. They help identify critical nodes (hubs, bottlenecks) that have disproportionate impact on system stability and change propagation.

This plugin provides two types of centrality measures:

  1. Degree Centrality (Fan-in/Fan-out) - Based on direct connections
  2. Betweenness Centrality - Based on position in shortest paths
  • centrality.module.max_fan_in: Maximum in-degree (most depended-upon module)
  • centrality.module.max_fan_out: Maximum out-degree (module with most dependencies)
  • centrality.module.avg_fan_in: Average in-degree across all modules
  • centrality.module.avg_fan_out: Average out-degree across all modules
  • centrality.module.median_fan_in: Median in-degree (robust to outliers)
  • centrality.module.median_fan_out: Median out-degree (robust to outliers)
  • centrality.module.hub_count: Number of hub modules (fan-in + fan-out > 10) - legacy metric

Hub-like dependencies are modules that exhibit both high in-degree AND high out-degree, making them critical architectural nodes that require special attention.

  • centrality.module.hub_like_count: Total number of hub-like dependency modules
  • centrality.module.hub_like_severe_count: Severe hubs (95th percentile + high betweenness)
  • centrality.module.hub_like_moderate_count: Moderate hubs (90th percentile)
  • centrality.module.hub_like_mild_count: Mild hubs (75th percentile)

Hub detection uses statistical thresholds (percentiles) rather than fixed values, making it adaptive to your codebase size. A hub must have both high fan-in and high fan-out, distinguishing it from simple utility modules (high fan-in only) or fragile modules (high fan-out only).

  • centrality.module.betweenness_max: Maximum betweenness centrality value
  • centrality.module.betweenness_avg: Average betweenness centrality
  • centrality.module.high_betweenness_count: Number of nodes with high betweenness (> 0.1)

Indicates:

  • Core utility or shared infrastructure
  • High reusability (good)
  • Single point of failure (risk)
  • Breaking changes affect many modules

Examples:

  • Shared utilities (utils/, common/)
  • Core types/interfaces
  • Configuration modules

Indicates:

  • Fragile module (depends on many things)
  • High coupling (risk)
  • Likely to break when dependencies change
  • Difficult to test in isolation

Examples:

  • Orchestration modules
  • Feature modules that aggregate functionality
  • Legacy code with accumulated dependencies

Hub-like Dependencies (High Fan-in + Fan-out)

Section titled “Hub-like Dependencies (High Fan-in + Fan-out)”

Indicates:

  • Critical architectural nodes that both depend on many modules AND are depended upon by many
  • High impact on system stability and change propagation
  • Potential bottlenecks and single points of failure
  • Require careful design, comprehensive testing, and documentation

Severity Levels:

  • Severe: Both fan-in and fan-out exceed 95th percentile AND high betweenness (>0.1) - immediate refactoring priority
  • Moderate: Both fan-in and fan-out exceed 90th percentile - monitor closely
  • Mild: Both fan-in and fan-out exceed 75th percentile - consider splitting if growing

Why this matters: Unlike simple high fan-in (utilities) or high fan-out (fragile modules), hub-like dependencies combine both characteristics, making them architectural hotspots where changes ripple through the system in multiple directions.

Indicates:

  • Module acts as a “bridge” between parts of the system
  • Many dependency paths flow through this node
  • Critical bottleneck - if it breaks, many paths are disrupted
  • Good candidate for refactoring to reduce coupling
MetricGoodWarningCritical
max_fan_in< 1515-30> 30
max_fan_out< 1010-20> 20
avg_fan_in< 33-5> 5
avg_fan_out< 22-4> 4
hub_count< 33-10> 10
hub_like_count< 22-5> 5
hub_like_severe_count01> 1
MetricGoodWarningCritical
betweenness_max< 0.050.05-0.15> 0.15
betweenness_avg< 0.010.01-0.03> 0.03
high_betweenness_count< 22-5> 5

Well-balanced system:

centrality.module.max_fan_in: 12
centrality.module.max_fan_out: 8
centrality.module.avg_fan_in: 2.3
centrality.module.avg_fan_out: 1.8
centrality.module.hub_count: 2
centrality.module.betweenness_max: 0.04
centrality.module.high_betweenness_count: 1

Dependencies are distributed, no single module dominates.

Hub-heavy system:

centrality.module.max_fan_in: 45
centrality.module.max_fan_out: 28
centrality.module.avg_fan_in: 4.2
centrality.module.avg_fan_out: 3.1
centrality.module.hub_count: 15
centrality.module.betweenness_max: 0.22
centrality.module.high_betweenness_count: 8

Too many critical nodes; system is fragile.

policy:
invariants:
- metric: centrality.module.max_fan_in
op: "<="
value: 15
- metric: centrality.module.max_fan_out
op: "<="
value: 10
- metric: centrality.module.hub_count
op: "<="
value: 3
- metric: centrality.module.hub_like_count
op: "<="
value: 2
- metric: centrality.module.hub_like_severe_count
op: "=="
value: 0
- metric: centrality.module.betweenness_max
op: "<="
value: 0.05
policy:
invariants:
- metric: centrality.module.max_fan_in
op: "<="
value: 30
- metric: centrality.module.max_fan_out
op: "<="
value: 20
- metric: centrality.module.hub_count
op: "<="
value: 10
- metric: centrality.module.hub_like_count
op: "<="
value: 5
- metric: centrality.module.hub_like_severe_count
op: "<="
value: 1
- metric: centrality.module.betweenness_max
op: "<="
value: 0.15
policy:
baseline:
mode: git
ref: origin/main
no_regression: true # Hub count and max fan-out must not increase

Check centrality entries in metric data:

  • centrality.module.nodes_table: Per-module fan-in/fan-out, betweenness, closeness, and PageRank
  • centrality.edge.top_bottlenecks_table: Top dependency bridge edges by edge betweenness
  • findings: Prioritized hub/bottleneck recommendations with evidence

Extract shared code:

// Before: Module depends on 15 different utilities
import { util1 } from './utils/util1';
import { util2 } from './utils/util2';
// ... 13 more imports
// After: Single facade
import { utils } from './utils';

Use dependency injection:

// Before: Direct dependencies
class Service {
constructor() {
this.db = new Database();
this.cache = new Cache();
this.logger = new Logger();
}
}
// After: Injected dependencies
class Service {
constructor(deps: ServiceDependencies) {
this.db = deps.db;
this.cache = deps.cache;
this.logger = deps.logger;
}
}

Apply facade pattern:

// Create a single interface for related functionality
export { api } from './api-facade';

Split large utilities:

// Before: One giant utils module
// utils/index.ts (imported by 40 modules)
// After: Domain-specific utilities
// utils/string.ts
// utils/date.ts
// utils/validation.ts

Create focused interfaces:

types/user.ts
// Instead of one large types module, split by domain
// types/product.ts
// types/order.ts

Break critical paths:

Before: A → B → C → D
E → B → F
G → B → H
(B has high betweenness)
After: A → C → D
E → F
G → H
(Remove B, create direct connections)

Use event-driven architecture:

// Instead of direct dependencies through a hub
// Use events/messages
eventBus.emit('user.created', userData);

Hub modules (high fan-in + fan-out) need special attention:

  • Add comprehensive tests
  • Document dependencies clearly
  • Consider splitting if too large
  • Use dependency injection for testability

High fan-in modules are often core infrastructure:

# Look for modules with fan-in > 20
centrality.module.max_fan_in: 25
# Inspect centrality.module.nodes_table and sort by fan_in

High fan-out modules are fragile:

# Modules with fan-out > 15 are risky
centrality.module.max_fan_out: 18
# Inspect centrality.module.nodes_table and sort by fan_out

High betweenness indicates bottlenecks:

# Nodes with betweenness > 0.1 are critical
centrality.module.betweenness_max: 0.15
centrality.module.high_betweenness_count: 3
# Inspect centrality.module.nodes_table and sort by betweenness

Combine metrics to prioritize:

  1. High priority: High fan-out + high betweenness (fragile bottleneck)
  2. Medium priority: High fan-in + high betweenness (critical hub)
  3. Low priority: High fan-in only (stable core)

Degree centrality is one of the simplest and most intuitive centrality measures in network analysis. It’s been used in software engineering since the 1990s to identify:

  • God classes (high fan-out)
  • Utility classes (high fan-in)
  • Hub components

Betweenness centrality was introduced by Freeman (1977) in social network analysis and adapted for software dependency graphs. It measures:

“The extent to which a node lies on paths between other nodes”

In software architecture:

  • High betweenness = critical dependency path
  • Removing high-betweenness nodes can break many connections
  • Useful for identifying refactoring targets

Algorithm: Brandes’ algorithm (O(V × E) for unweighted graphs)

  • Algorithm:
    • Degree: Direct neighbor counting (O(V + E))
    • Betweenness: Brandes’ algorithm (O(V × E))
  • Requires: Import graph
  • Cache: Results are cached; recomputed only when graph changes
  • Normalization: Betweenness is normalized by (n-1)(n-2) for directed graphs
  • Propagation Cost: High fan-out increases propagation cost
  • SCC: Hub modules often participate in cycles
  • Core-Periphery: High fan-in nodes are often in the core
  • Modularity Q: Hub modules can reduce modularity scores
  • Freeman, L. C. (1977). “A set of measures of centrality based on betweenness”
  • Brandes, U. (2001). “A faster algorithm for betweenness centrality”
  • Martin, R. C. (2002). “Agile Software Development: Principles, Patterns, and Practices”
  • Lanza, M., & Marinescu, R. (2006). “Object-Oriented Metrics in Practice”