Measuring the true length and complexity of functions
Logical Lines of Code (LOC) counts the number of executable statements in a function or method, excluding comments, blank lines, and braces. This gives a more accurate measure of function length than simply counting physical lines.
Long functions are harder to understand, test, and maintain. They often indicate that a function is trying to do too much and should be broken down into smaller, focused pieces.
The calculation focuses on meaningful statements that perform actual logic, ignoring formatting and documentation.
Count only the executable statements, not comments or blank lines.
/**
* Calculate the area of a rectangle
* This is JSDoc - doesn't count
*/
function calculateArea(width, height) {
// This comment doesn't count
const area = width * height; // Statement 1
console.log(area); // Statement 2
return area; // Statement 3
}
// Physical Lines: 11
// Logical LOC: 3Control structures count as statements.
function validateUser(user) {
if (!user) { // Statement 1: if condition
return false; // Statement 2: return
}
const hasEmail = // Statement 3: variable declaration
user.email && user.email.includes("@");
if (!hasEmail) { // Statement 4: if condition
return false; // Statement 5: return
}
const isAdult = // Statement 6: variable declaration
user.age >= 18;
return isAdult; // Statement 7: return
}
// Physical Lines: 17
// Logical LOC: 7Method chains typically count as a single statement.
function getActiveUserNames(users) {
return users // Statement 1: entire chain
.filter(u => u.isActive) // (part of statement 1)
.map(u => u.name) // (part of statement 1)
.sort(); // (part of statement 1)
}
// Physical Lines: 6
// Logical LOC: 1
// Compare to loop version:
function getActiveUserNamesLoop(users) {
const result = []; // Statement 1
for (const user of users) { // Statement 2
if (user.isActive) { // Statement 3
result.push(user.name); // Statement 4
}
}
result.sort(); // Statement 5
return result; // Statement 6
}
// Physical Lines: 10
// Logical LOC: 6Long functions become hard to understand and maintain.
function processOrder(order) {
// Validate order - 10 statements
if (!order) throw new Error("No order"); // 1
if (!order.items || order.items.length === 0) { // 2
throw new Error("No items"); // 3
}
if (!order.customer) throw new Error("No customer"); // 4
if (!order.customer.email) throw new Error("No email"); // 5
// Calculate totals - 12 statements
let subtotal = 0; // 6
for (const item of order.items) { // 7
if (!item.price || !item.quantity) continue; // 8
subtotal += item.price * item.quantity; // 9
}
const tax = subtotal * 0.08; // 10
const shipping = subtotal > 100 ? 0 : 10; // 11
const total = subtotal + tax + shipping; // 12
// Apply discounts - 8 statements
let discount = 0; // 13
if (order.customer.type === "premium") { // 14
discount = total * 0.1; // 15
} else if (order.customer.type === "gold") { // 16
discount = total * 0.05; // 17
}
const finalTotal = total - discount; // 18
// Update inventory - 6 statements
for (const item of order.items) { // 19
const product = findProduct(item.productId); // 20
if (product) { // 21
product.quantity -= item.quantity; // 22
saveProduct(product); // 23
}
}
// Send notifications - 9 statements
const notification = { // 24
to: order.customer.email,
subject: "Order Confirmed",
body: `Total: $${finalTotal}`,
};
try { // 25
sendEmail(notification); // 26
logSuccess(order.id); // 27
} catch (error) { // 28
logError(error); // 29
throw error; // 30
}
return { orderId: order.id, total: finalTotal }; // 31
}
// Logical LOC: ~31 (actual count may be higher with all statements)
// Way too long - should be split into multiple functions!Breaking into focused functions keeps each function short and clear.
// Main orchestrator: Logical LOC = 5
function processOrder(order) {
validateOrder(order); // 1
const totals = calculateOrderTotals(order); // 2
updateInventory(order.items); // 3
sendOrderConfirmation(order, totals.final); // 4
return { orderId: order.id, total: totals.final }; // 5
}
// Validation: Logical LOC = 5
function validateOrder(order) {
if (!order) throw new Error("No order"); // 1
if (!order.items?.length) throw new Error("No items"); // 2
if (!order.customer) throw new Error("No customer"); // 3
if (!order.customer.email) throw new Error("No email"); // 4
}
// Calculate totals: Logical LOC = 8
function calculateOrderTotals(order) {
const subtotal = order.items.reduce((sum, item) => // 1
sum + (item.price * item.quantity), 0);
const tax = subtotal * 0.08; // 2
const shipping = subtotal > 100 ? 0 : 10; // 3
const discount = getDiscount(order.customer, subtotal); // 4
const final = subtotal + tax + shipping - discount; // 5
return { subtotal, tax, shipping, discount, final }; // 6
}
// Get discount: Logical LOC = 6
function getDiscount(customer, subtotal) {
const discountRates = { // 1
premium: 0.1,
gold: 0.05,
};
const rate = discountRates[customer.type] || 0; // 2
return subtotal * rate; // 3
}
// Update inventory: Logical LOC = 6
function updateInventory(items) {
for (const item of items) { // 1
const product = findProduct(item.productId); // 2
if (product) { // 3
product.quantity -= item.quantity; // 4
saveProduct(product); // 5
}
}
}
// Send notification: Logical LOC = 8
function sendOrderConfirmation(order, total) {
const notification = { // 1
to: order.customer.email,
subject: "Order Confirmed",
body: `Total: $${total}`,
};
try { // 2
sendEmail(notification); // 3
logSuccess(order.id); // 4
} catch (error) { // 5
logError(error); // 6
throw error; // 7
}
}
// Maximum LOC per function: 8
// Each function has a single, clear purpose!| Sensitivity Level | Max LOC | Use Case |
|---|---|---|
| High | ≤ 50 | Highly maintainable code, microservices |
| Medium | ≤ 150 | Most applications, balanced approach |
| Low | ≤ 300 | Legacy codebases, gradual improvement |
A good rule of thumb: if a function doesn't fit on your screen without scrolling, it's probably too long. Most developers can comfortably view 20-50 lines at once, which corresponds roughly to the "High" sensitivity threshold.
Take a section of code and move it to its own function with a descriptive name.
Make functions read like well-named steps in a process.
function checkout() {
validateCart();
calculateTotals();
processPayment();
updateInventory();
sendConfirmation();
}Use array methods (filter, map, reduce) instead of explicit loops to reduce LOC while maintaining clarity.
If a function has many parameters or local variables, it might be doing too much. Group related data into objects and extract helper functions.