Measuring decision points and code paths in your functions
Branch count measures the number of decision points in your code. Each decision point creates a branch where the code execution can go in different directions. This metric is closely related to cyclomatic complexity, which is a widely-used measure of code complexity.
Higher branch counts mean more possible execution paths through your code, which makes it harder to test thoroughly, increases the likelihood of bugs, and makes the code more difficult to understand.
The branch count increments by 1 for each of the following control flow constructs:
if statementelse if clausefor loopwhile loopdo-while loopcase in a switch statementcatch block? :)&&, ||)A function with no branches (just sequential statements) has a branch count of 0.
A simple function with only sequential statements has no branches.
function calculateArea(width, height) {
const area = width * height;
console.log("Area calculated");
return area;
}
// Branch Count: 0
// No decision points, just sequential executionAn if-else statement creates 2 branches.
function checkAge(age) {
if (age >= 18) { // Branch 1: if
return "Adult";
} else { // Branch 2: else (implicit)
return "Minor";
}
}
// Branch Count: 2
// Two possible paths through the functionMultiple if-else if conditions increase the branch count.
function getGrade(score) {
if (score >= 90) { // Branch 1
return "A";
} else if (score >= 80) { // Branch 2
return "B";
} else if (score >= 70) { // Branch 3
return "C";
} else { // Branch 4 (implicit)
return "F";
}
}
// Branch Count: 4
// Four different execution pathsLogical AND (&&) and OR (||) operators create additional branches.
function canVote(age, citizen) {
if (age >= 18 && citizen) { // Branch 1: if
// Branch 2: && (short-circuit)
return true;
}
return false;
}
// Branch Count: 2
// 1 for 'if', 1 for '&&'
function isValidEmail(email) {
if (!email || !email.includes("@")) { // Branch 1: if
// Branch 2: || (short-circuit)
return false;
}
return true;
}
// Branch Count: 2Each case in a switch statement counts as a branch.
function getDayType(day) {
switch (day) {
case "Monday": // Branch 1
case "Tuesday": // Branch 2
case "Wednesday": // Branch 3
case "Thursday": // Branch 4
case "Friday": // Branch 5
return "Weekday";
case "Saturday": // Branch 6
case "Sunday": // Branch 7
return "Weekend";
default: // Branch 8
return "Invalid";
}
}
// Branch Count: 8
// Each case clause is a decision pointComplex business logic can quickly accumulate many branches.
function calculateShipping(order) {
let cost = 0;
if (order.country === "USA") { // Branch 1
if (order.state === "AK" || order.state === "HI") { // Branch 2, 3
cost = 25;
} else if (order.total > 100) { // Branch 4
cost = 0; // Free shipping
} else {
cost = 10;
}
} else if (order.country === "Canada") { // Branch 5
if (order.total > 150) { // Branch 6
cost = 15;
} else {
cost = 25;
}
} else {
cost = 50;
}
if (order.isPriority && cost > 0) { // Branch 7, 8
cost *= 2;
}
if (order.hasInsurance) { // Branch 9
cost += 5;
}
if (order.isGift) { // Branch 10
cost += 3;
}
return cost;
}
// Branch Count: 12
// Too many paths - hard to test and maintain!Breaking into smaller functions reduces branch count per function.
// Main function: Branch Count = 3
function calculateShipping(order) {
let cost = getBaseCost(order);
if (order.isPriority && cost > 0) { // Branch 1, 2
cost *= 2;
}
cost += getAddOns(order);
return cost;
}
// Helper function: Branch Count = 5
function getBaseCost(order) {
if (order.country === "USA") { // Branch 1
return getUSAShipping(order);
} else if (order.country === "Canada") { // Branch 2
return order.total > 150 ? 15 : 25; // Branch 3
}
return 50;
}
// Helper function: Branch Count = 3
function getUSAShipping(order) {
const isRemote = order.state === "AK" || order.state === "HI"; // Branch 1, 2
if (isRemote) { // Branch 3
return 25;
}
return order.total > 100 ? 0 : 10;
}
// Helper function: Branch Count = 2
function getAddOns(order) {
let addOn = 0;
if (order.hasInsurance) { // Branch 1
addOn += 5;
}
if (order.isGift) { // Branch 2
addOn += 3;
}
return addOn;
}
// Maximum Branch Count per function: 5
// Much easier to test and understand!To achieve 100% branch coverage, you need to test every possible path. The number of test cases grows exponentially with branch count:
| Branch Count | Minimum Test Cases |
|---|---|
| 2 | 2-4 tests |
| 5 | 6-32 tests |
| 10 | 11-1,024 tests |
| 20 | 21-1,048,576 tests |
| Sensitivity Level | Max Branches | Use Case |
|---|---|---|
| High | ≤ 5 | Critical paths, security code |
| Medium | ≤ 15 | Most business logic |
| Low | ≤ 25 | Legacy systems being improved |
Break complex conditionals into separate, well-named functions. Each function should have a single, clear purpose.
Replace long if-else or switch statements with data structures.
// Before: 7 branches
function getDiscount(customerType) {
if (customerType === "gold") return 0.20;
else if (customerType === "silver") return 0.15;
else if (customerType === "bronze") return 0.10;
// ... more cases
}
// After: 0 branches
const DISCOUNTS = {
gold: 0.20,
silver: 0.15,
bronze: 0.10,
// ... more mappings
};
function getDiscount(customerType) {
return DISCOUNTS[customerType] || 0;
}For complex conditional logic, use object-oriented design patterns to encapsulate different behaviors.
Reduce nested conditions by combining or inverting logic.
// Before: 3 branches
if (user.isActive) {
if (user.hasPermission) {
if (user.emailVerified) {
grantAccess();
}
}
}
// After: 1 branch
if (user.isActive && user.hasPermission && user.emailVerified) {
grantAccess();
}