Initial commit
This commit is contained in:
364
skills/c4model-c4/complexity-metrics.md
Normal file
364
skills/c4model-c4/complexity-metrics.md
Normal file
@@ -0,0 +1,364 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user