365 lines
7.8 KiB
Markdown
365 lines
7.8 KiB
Markdown
# Complexity Metrics Methodology
|
|
|
|
This guide provides methodology for calculating and documenting code complexity metrics at the C4 Code level.
|
|
|
|
---
|
|
|
|
## Metrics Overview
|
|
|
|
| Metric | Description | Threshold |
|
|
|--------|-------------|-----------|
|
|
| Lines of Code (LOC) | Actual code lines | Warning: >30, Critical: >50 |
|
|
| Cyclomatic Complexity | Decision paths | Warning: >6, Critical: >10 |
|
|
| Cognitive Complexity | Mental effort | Warning: >10, Critical: >20 |
|
|
| Parameter Count | Function parameters | Warning: >3, Critical: >5 |
|
|
| Nesting Depth | Max nesting level | Warning: >3, Critical: >5 |
|
|
|
|
---
|
|
|
|
## Lines of Code (LOC)
|
|
|
|
### Definition
|
|
Count of actual code lines excluding:
|
|
- Blank lines
|
|
- Comments
|
|
- Import statements (debatable)
|
|
|
|
### Calculation
|
|
|
|
```typescript
|
|
// Example function - LOC = 8
|
|
function processOrder(order: Order): ProcessedOrder {
|
|
const validated = validateOrder(order); // 1
|
|
if (!validated) { // 2
|
|
throw new Error('Invalid order'); // 3
|
|
} // 4
|
|
const enriched = enrichOrder(validated); // 5
|
|
const processed = applyRules(enriched); // 6
|
|
logOrder(processed); // 7
|
|
return processed; // 8
|
|
}
|
|
```
|
|
|
|
### Thresholds
|
|
|
|
| Range | Rating | Action |
|
|
|-------|--------|--------|
|
|
| 1-30 | Good | No action needed |
|
|
| 31-50 | Warning | Consider extracting methods |
|
|
| >50 | Critical | Must refactor |
|
|
|
|
---
|
|
|
|
## Cyclomatic Complexity
|
|
|
|
### Definition
|
|
Number of linearly independent paths through code. Calculated as:
|
|
- Start with 1
|
|
- Add 1 for each: `if`, `else if`, `case`, `for`, `while`, `do`, `catch`, `&&`, `||`, `?:`
|
|
|
|
### Calculation Example
|
|
|
|
```typescript
|
|
function processPayment(payment: Payment): Result { // CC = 1 (base)
|
|
if (payment.amount <= 0) { // CC = 2
|
|
return { error: 'Invalid amount' };
|
|
}
|
|
|
|
if (payment.type === 'credit') { // CC = 3
|
|
if (payment.amount > 10000) { // CC = 4
|
|
return { error: 'Amount exceeds limit' };
|
|
}
|
|
return processCreditCard(payment);
|
|
} else if (payment.type === 'debit') { // CC = 5
|
|
return processDebitCard(payment);
|
|
} else if (payment.type === 'crypto') { // CC = 6
|
|
return processCrypto(payment);
|
|
}
|
|
|
|
return { error: 'Unknown payment type' };
|
|
}
|
|
// Total Cyclomatic Complexity = 6
|
|
```
|
|
|
|
### Decision Points
|
|
|
|
| Construct | Adds |
|
|
|-----------|------|
|
|
| `if` | +1 |
|
|
| `else if` | +1 |
|
|
| `case` (switch) | +1 per case |
|
|
| `for` | +1 |
|
|
| `while` | +1 |
|
|
| `do...while` | +1 |
|
|
| `catch` | +1 |
|
|
| `&&` | +1 |
|
|
| `||` | +1 |
|
|
| `?:` (ternary) | +1 |
|
|
| `?.` (optional chain) | +1 |
|
|
| `??` (nullish coalesce) | +1 |
|
|
|
|
### Thresholds
|
|
|
|
| Range | Rating | Interpretation |
|
|
|-------|--------|----------------|
|
|
| 1-6 | Good | Simple, easy to test |
|
|
| 7-10 | Warning | Moderate complexity |
|
|
| 11-20 | High | Difficult to test |
|
|
| >20 | Critical | Untestable, must refactor |
|
|
|
|
---
|
|
|
|
## Cognitive Complexity
|
|
|
|
### Definition
|
|
A metric that measures the mental effort required to understand code. Unlike cyclomatic complexity, it:
|
|
- Penalizes nesting
|
|
- Ignores shorthand structures
|
|
- Focuses on readability
|
|
|
|
### Calculation Rules
|
|
|
|
1. **Increment for each:**
|
|
- `if`, `else if`, `else`
|
|
- `switch`
|
|
- `for`, `foreach`, `while`, `do`
|
|
- `catch`
|
|
- `break`/`continue` to label
|
|
- Sequences of logical operators
|
|
|
|
2. **Nesting penalty:**
|
|
- Add +1 for each level of nesting when incrementing
|
|
|
|
### Example
|
|
|
|
```typescript
|
|
function example(arr: number[], target: number): number {
|
|
// Base: 0
|
|
for (const item of arr) { // +1 (increment)
|
|
if (item === target) { // +2 (increment + 1 nesting)
|
|
return item;
|
|
} else if (item > target) { // +2 (increment + 1 nesting)
|
|
if (item % 2 === 0) { // +3 (increment + 2 nesting)
|
|
return item * 2;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
// Total Cognitive Complexity = 8
|
|
```
|
|
|
|
### Thresholds
|
|
|
|
| Range | Rating | Action |
|
|
|-------|--------|--------|
|
|
| 0-10 | Good | Understandable |
|
|
| 11-20 | Warning | Getting complex |
|
|
| >20 | Critical | Hard to understand |
|
|
|
|
---
|
|
|
|
## Parameter Count
|
|
|
|
### Definition
|
|
Number of parameters a function accepts.
|
|
|
|
### Guidelines
|
|
|
|
| Count | Rating | Recommendation |
|
|
|-------|--------|----------------|
|
|
| 0-3 | Good | Ideal range |
|
|
| 4-5 | Warning | Consider parameter object |
|
|
| >5 | Critical | Use parameter object |
|
|
|
|
### Refactoring Example
|
|
|
|
```typescript
|
|
// Before: 6 parameters (Critical)
|
|
function createUser(
|
|
name: string,
|
|
email: string,
|
|
age: number,
|
|
address: string,
|
|
phone: string,
|
|
role: string
|
|
) { }
|
|
|
|
// After: 1 parameter object (Good)
|
|
interface CreateUserInput {
|
|
name: string;
|
|
email: string;
|
|
age: number;
|
|
address: string;
|
|
phone: string;
|
|
role: string;
|
|
}
|
|
|
|
function createUser(input: CreateUserInput) { }
|
|
```
|
|
|
|
---
|
|
|
|
## Nesting Depth
|
|
|
|
### Definition
|
|
Maximum depth of nested control structures.
|
|
|
|
### Calculation
|
|
|
|
```typescript
|
|
function process(data: Data): Result {
|
|
if (data.valid) { // Depth 1
|
|
for (const item of data.items) { // Depth 2
|
|
if (item.active) { // Depth 3
|
|
try { // Depth 4
|
|
if (item.type === 'A') { // Depth 5 (Critical!)
|
|
// ...
|
|
}
|
|
} catch (e) {
|
|
// ...
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
// Maximum Nesting Depth = 5
|
|
```
|
|
|
|
### Thresholds
|
|
|
|
| Depth | Rating | Action |
|
|
|-------|--------|--------|
|
|
| 0-3 | Good | Easy to follow |
|
|
| 4-5 | Warning | Consider extracting |
|
|
| >5 | Critical | Must refactor |
|
|
|
|
### Refactoring Techniques
|
|
|
|
1. **Early returns (guard clauses)**
|
|
```typescript
|
|
// Before
|
|
function process(data) {
|
|
if (data) {
|
|
if (data.valid) {
|
|
// actual logic
|
|
}
|
|
}
|
|
}
|
|
|
|
// After
|
|
function process(data) {
|
|
if (!data) return;
|
|
if (!data.valid) return;
|
|
// actual logic
|
|
}
|
|
```
|
|
|
|
2. **Extract nested logic**
|
|
```typescript
|
|
// Before
|
|
for (const item of items) {
|
|
if (item.active) {
|
|
if (item.valid) {
|
|
// complex logic
|
|
}
|
|
}
|
|
}
|
|
|
|
// After
|
|
function processItem(item) {
|
|
if (!item.active || !item.valid) return;
|
|
// complex logic
|
|
}
|
|
|
|
for (const item of items) {
|
|
processItem(item);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Metrics JSON Structure
|
|
|
|
```json
|
|
{
|
|
"metrics": {
|
|
"lines_of_code": 45,
|
|
"cyclomatic_complexity": 8,
|
|
"cognitive_complexity": 15,
|
|
"parameter_count": 3,
|
|
"nesting_depth": 4
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Threshold Summary Table
|
|
|
|
| Metric | Good | Warning | Critical |
|
|
|--------|------|---------|----------|
|
|
| LOC | 1-30 | 31-50 | >50 |
|
|
| Cyclomatic | 1-6 | 7-10 | >10 |
|
|
| Cognitive | 0-10 | 11-20 | >20 |
|
|
| Parameters | 0-3 | 4-5 | >5 |
|
|
| Nesting | 0-3 | 4-5 | >5 |
|
|
|
|
---
|
|
|
|
## Observation Examples
|
|
|
|
### High Complexity Warning
|
|
|
|
```json
|
|
{
|
|
"id": "obs-auth-service-complexity-01",
|
|
"category": "complexity",
|
|
"severity": "warning",
|
|
"description": "Cyclomatic complexity of 8 exceeds recommended threshold of 6. Consider extracting authentication steps into separate methods.",
|
|
"evidence": [
|
|
{
|
|
"location": "src/services/auth.ts:45-120",
|
|
"type": "metric"
|
|
}
|
|
],
|
|
"tags": ["complexity", "refactoring-candidate"]
|
|
}
|
|
```
|
|
|
|
### Critical Nesting
|
|
|
|
```json
|
|
{
|
|
"id": "obs-order-processor-nesting-01",
|
|
"category": "complexity",
|
|
"severity": "critical",
|
|
"description": "Maximum nesting depth of 6 makes code difficult to understand. Extract nested logic into helper functions.",
|
|
"evidence": [
|
|
{
|
|
"location": "src/services/order.ts:78-150",
|
|
"type": "code",
|
|
"snippet": "if (order.valid) { for (...) { if (...) { try { if (...) {"
|
|
}
|
|
],
|
|
"tags": ["nesting", "refactoring-required"]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
### ✅ DO:
|
|
- Calculate all five metrics for functions/methods
|
|
- Flag any metric exceeding warning threshold
|
|
- Provide specific refactoring suggestions
|
|
- Include evidence with line numbers
|
|
|
|
### ❌ DON'T:
|
|
- Skip metrics for simple functions
|
|
- Ignore nesting depth
|
|
- Accept critical thresholds without observation
|
|
- Forget to suggest refactoring approaches
|