June 17, 2026 in code-smells5 minutes
A intermediate-level guide to Long Method: before-and-after java code and diagrams for a CS student.
Imagine you are reviewing a colleague’s work. You open a single method and find yourself scrolling constantly just to understand what it does. By the time you reach the bottom, you have forgotten the logic that started at the top. This is often caused by high “Cognitive Load”—the amount of mental effort required to process information. When a method tries to do too many things at once, your brain has to track dozens of local variables and multiple logical stages simultaneously, making it nearly impossible to spot bugs or understand the intent.
public class InvoiceProcessor {
public void processInvoice(Order order) {
// Step 1: Validate Order
if (order.items().isEmpty()) {
throw new IllegalArgumentException("Order must have items");
}
if (order.getCustomer() == null) {
throw new IllegalArgumentException("Customer is required");
}
// Step 2: Calculate Totals
double subtotal = 0;
for (Item item : order.items()) {
subtotal += item.price() * item.quantity();
}
double taxRate = 0.08;
if (order.isInternational()) {
taxRate = 0.15;
}
double taxAmount = subtotal * taxRate;
double total = subtotal + taxAmount;
// Step 3: Apply Discounts
if (total > 100.0) {
total -= (total * 0.10);
}
// Step 4: Generate Receipt
System.out.println("Invoice for: " + order.getCustomer().name());
System.out.println("Subtotal: $" + subtotal);
System.out.println("Tax: $" + taxAmount);
System.out.println("Total: $" + total);
}
}The processInvoice method above is suffering from the Long Method smell. Instead of being a high-level summary of an invoice’s lifecycle, it is a monolithic block of instructions that mixes validation, math, and output.
classDiagram
class Order {
+items() List~Item~
+getCustomer() Customer
+isInternational() boolean
}
class Item {
+price() double
+quantity() int
}
class Customer {
+name() String
}
class InvoiceProcessor {
+processInvoice(Order order) void
}
InvoiceProcessor ..> Order : uses
Order *-- Item : contains
Order --> Customer : has
How do you know when a method has grown too large? Look for these physical symptoms in your IDE:
if statements and loops, it is difficult to track which “else” belongs to which “if.”// Calculate tax or // Print receipt to label blocks of code within a method, those comments are actually screaming for the code they describe to be moved into its own named function.To fix a Long Method, we use a refactoring technique called Extract Method. You identify a coherent chunk of code within your large method, move it into a new, smaller method, and replace the original code with a call to that new method.
This creates a “Composed Method”—a high-level method that reads like a table of contents, delegating the gritty details to specialized sub-methods.
public class InvoiceProcessor {
public void processInvoice(Order order) {
validateOrder(order);
double subtotal = calculateSubtotal(order);
double taxAmount = calculateTax(subtotal, order.isInternational());
double total = applyDiscounts(subtotal + taxAmount);
printReceipt(order.getCustomer().name(), subtotal, taxAmount, total);
}
private void validateOrder(Order order) {
if (order.items().isEmpty()) {
throw new IllegalArgumentException("Order must have items");
}
if (order.getCustomer() == null) {
throw new IllegalArgumentException("Customer is required");
}
}
private double calculateSubtotal(Order order) {
return order.items().stream()
.mapToDouble(i -> i.price() * i.quantity())
.sum();
}
private double calculateTax(double subtotal, boolean isInternational) {
double taxRate = isInternational ? 0.15 : 0.08;
return subtotal * taxRate;
}
private double applyDiscounts(double amount) {
if (amount > 100.0) {
return amount * 0.90;
}
return amount;
}
private void printReceipt(String name, double subtotal, double tax, double total) {
System.out.println("Invoice for: " + name);
System.out.println("Subtotal: $" + subtotal);
System.out.println("Tax: $" + tax);
System.out.println("Total: $" + total);
}
}By extracting methods like validateOrder, calculateSubtotal, and printReceipt, we have transformed the logic into a readable story. The processInvoice method no longer cares how tax is calculated; it only cares that the calculation happens.
sequenceDiagram
participant IP as InvoiceProcessor
participant O as Order
IP->>O: validateOrder(order)
IP->>O: calculateSubtotal(order)
IP->>O: calculateTax(subtotal, isInternational())
IP->>O: applyDiscounts(amount)
IP->>O: printReceipt(name, subtotal, tax, total)
Extract Method is one of the most frequently used refactorings because it makes code “self-documenting.” You no longer need a comment to explain what five lines of math are doing if you can simply name that method calculateTax.
However, be wary of fragmentation. If you extract every single line into its own tiny method, you might end up with an overly complex web where it is hard to follow the program’s flow because you are constantly jumping between dozens of different locations in the file. Aim for “logical chunks”—groups of code that represent a single, clear action or calculation.
Using the refactored Composed Method approach
InvoiceProcessor processor = new InvoiceProcessor();
processor.processInvoice(myOrder);