code complexity & repetition analysis tool
Cyclomatic Complexity#
What is Cyclomatic Complexity?#
Cyclomatic Complexity (CC), introduced by Thomas McCabe in 1976, measures the number of independent paths through a program's source code. It provides a quantitative measure of code complexity.
How It Works#
Mccabre uses a simplified formula:
CC = (number of decision points) + 1
Decision Points#
A decision point is any control flow statement that creates a branch:
if,else ifwhile,for,loopswitch,match,casecatch- Logical operators:
&&,|| - Ternary operator:
?
Example#
function checkUser(user) { // CC starts at 1
if (!user) { // +1 = 2
return false;
}
if (user.age > 18 && user.verified) { // +1 (if) +1 (&&) = 4
return true;
}
return false;
}
// Total CC = 4
Interpretation#
| CC Range | Risk Level | Recommendation |
|---|---|---|
| 1-10 | Low | Simple, easy to test |
| 11-20 | Moderate | Consider refactoring |
| 21-50 | High | Should refactor |
| 50+ | Very High | Urgent refactoring needed |
Why It Matters#
Testing Complexity#
Higher CC means:
- More test cases needed for full coverage
- Higher chance of bugs
- Harder to understand and maintain
A function with CC=10 requires at least 10 test cases to cover all paths.
Maintenance Burden#
Complex functions are:
- Harder to modify without introducing bugs
- More difficult for new developers to understand
- More prone to subtle edge cases
Reducing Complexity#
Extract Methods#
Before (CC=8):
function processOrder(order) {
if (!order.id) throw new Error("No ID");
if (order.status === "cancelled") return null;
if (order.items.length === 0) throw new Error("No items");
let total = 0;
for (let item of order.items) {
if (item.price && item.quantity) {
total += item.price * item.quantity;
}
}
return total;
}
After (CC=3 + 3 = 6 total):
function processOrder(order) { // CC=3
validateOrder(order);
return calculateTotal(order);
}
function validateOrder(order) { // CC=3
if (!order.id) throw new Error("No ID");
if (order.status === "cancelled") return null;
if (order.items.length === 0) throw new Error("No items");
}
function calculateTotal(order) { // CC=2
let total = 0;
for (let item of order.items) {
if (item.price && item.quantity) {
total += item.price * item.quantity;
}
}
return total;
}
Use Early Returns#
Before:
fn check(x: i32) -> bool { // CC=3
let mut result = false;
if x > 0 {
if x < 100 {
result = true;
}
}
result
}
After:
fn check(x: i32) -> bool { // CC=3 (same but cleaner)
if x <= 0 { return false; }
if x >= 100 { return false; }
true
}
Replace Complex Conditions#
Before:
if ((user.role === "admin" || user.role === "moderator") &&
user.active && !user.suspended) { // CC contribution: 4
// ...
}
After:
function canModerate(user) { // CC=3
const isModerator = user.role === "admin" || user.role === "moderator";
return isModerator && user.active && !user.suspended;
}
if (canModerate(user)) { // CC=1
// ...
}
Using with Mccabre#
Check Specific Files#
mccabre complexity src/complex.js
Set Custom Threshold#
mccabre complexity . --threshold 15
JSON Output for CI#
mccabre complexity . --json | jq '.files[] | select(.cyclomatic.file_complexity > 20)'