Test coverage is a vanity metric.
There, I said it. After years of watching teams obsess over hitting 80% coverage while their velocity tanks, I'm convinced most code quality metrics are theater.
But some metrics genuinely predict whether you'll ship fast or slow. Let me show you which ones.
The Metrics That Lie
Test Coverage A codebase with 90% coverage can still be unmaintainable garbage. Coverage tells you tests exist. It says nothing about:
- Whether they test the right things
- Whether they catch real bugs
- Whether they slow down refactoring
I've seen teams with 95% coverage afraid to change anything because every modification breaks 50 tests.
Cyclomatic Complexity "Keep complexity under 10!" Sure, but a function with complexity 15 that's well-named and does one thing is better than 5 functions with complexity 3 that you have to trace through to understand.
Complexity without context is noise.
Lines of Code More lines ≠ worse code. Sometimes the explicit 20-line version is better than the clever 3-line version that nobody can read.
The Metrics That Matter
After analyzing hundreds of codebases through our platform, here's what actually correlates with team velocity:
1. Code Churn (Change Frequency)
// What we track in Glue
interface FileHealth {
path: string;
churnScore: number; // Changes per month
contributorCount: number; // How many people touch it
lastStableDate: Date; // When it stopped changing
}
Files that change constantly are either:
- Central to the product (expected)
- Poorly designed (problem)
The insight: High churn + many contributors = probable hotspot. These files need attention before they become bottlenecks.
2. Coupling Score
How many files change together? If every time you modify UserService.ts, you also have to change 8 other files, you have a coupling problem.
Coupling Analysis:
UserService.ts changes trigger:
- UserController.ts (92% correlation)
- UserRepository.ts (89% correlation)
- UserDTO.ts (67% correlation)
- NotificationService.ts (45% correlation) ← unexpected
- BillingService.ts (34% correlation) ← red flag
That BillingService coupling is a design smell. Why does user logic affect billing?
3. Blast Radius
Before any change, you should know: what could break?
// Our call graph analysis
const impact = await analyzeBlastRadius('updateUserProfile');
// Returns:
{
directCallers: 12,
transitiveCallers: 47,
affectedEndpoints: ['PUT /api/users', 'POST /api/sync'],
affectedTests: 23,
riskScore: 'high'
}
Functions with high blast radius need more careful changes. Functions with low blast radius can be modified confidently.
4. Knowledge Distribution
Who knows this code?
Knowledge Map for payments/:
alice: 67% of commits (primary owner)
bob: 22% of commits (secondary)
charlie: 11% of commits (occasional)
Risk: Single point of failure (alice)
If one person owns most of a critical module, that's organizational risk — not technical, but it affects velocity just as much.
5. API Surface Stability
How often do your interfaces change?
Stable internal APIs = teams can work independently. Constantly changing APIs = everyone's blocked waiting on everyone else.
API Stability (last 90 days):
/api/users/* - 2 breaking changes (stable)
/api/products/* - 0 breaking changes (very stable)
/api/checkout/* - 11 breaking changes (unstable)
That checkout API is killing velocity. Every change forces mobile, web, and partners to update.
Building a Quality Dashboard That Works
Here's how we structure code health in our platform:
interface CodeHealthScore {
overall: number; // 0-100
// Component scores
churn: number; // Lower is better
coupling: number; // Lower is better
testCoverage: number; // Higher is better (but weighted low)
blastRadius: number; // Context-dependent
knowledgeSpread: number; // Higher is better
// Status
status: 'healthy' | 'watch' | 'critical';
hotspots: string[]; // Files needing attention
}
The dashboard shows:
- 🟢 Healthy (70-100): Ship with confidence
- 🟡 Watch (50-69): Monitor these areas
- 🔴 Critical (0-49): Fix before adding features
The Actionable Part
Measuring is pointless without action. Here's the framework:
Weekly: Review hotspots (high churn + high coupling files) Monthly: Check knowledge distribution — any single points of failure? Quarterly: Assess API stability — which interfaces need investment?
For each critical issue, the question is: "Does fixing this unblock future velocity?"
If a file has high churn but it's your core algorithm that's legitimately complex, maybe that's fine. If a file has high churn because the abstraction is wrong, that's worth fixing.
What AI Changes About Measurement
Here's where it gets interesting. Traditional metrics require you to know what to look for. AI-powered analysis can surface issues you didn't know to ask about.
Our system automatically identifies:
- Unexpected dependencies: Why does auth code call analytics?
- Orphaned code: Functions that nothing calls anymore
- Architectural drift: New code that doesn't follow established patterns
- Implicit contracts: Behaviors that aren't documented but are depended upon
These are the insights that make the difference between "we have metrics" and "we understand our codebase."
Stop Measuring Theater
The goal isn't beautiful dashboards. It's shipping faster with fewer bugs.
If your metrics don't help you make decisions — which code to refactor, where to invest, what's blocking velocity — they're theater.
Start with churn and coupling. Those two metrics alone will show you where the real problems are. Everything else is refinement.