The Method That Resists a Verb
The Method That Resists a Verb
The Smell
A developer is writing a method in the logistics platform’s OrderService. The method receives an incoming order, validates the address, checks inventory availability, reserves stock, calculates the shipping rate, selects a carrier, and creates a pending shipment record. The developer needs a name. They try createOrder, but the method does more than create. They try processOrder, but that is meaninglessly vague. They try handleNewOrder, which is processOrder with a hat on. They settle on submitOrder and move on.
Six months later, a new developer reads the code. They see submitOrder and expect it to submit an order to some external system. It does not. It validates, checks inventory, reserves stock, calculates rates, and creates records. The name submitOrder communicated a wrong concept. But no correct name exists because no single verb describes six unrelated operations.
The naming difficulty was the signal. The developer treated it as a vocabulary problem. It was a design problem.
The Cognitive Cost
A method named processOrder forces every caller and every reviewer to hold the question “what does processOrder actually do?” in working memory. That question stays open until they read the implementation. In the logistics codebase, processOrder is called from 14 call sites. At each call site, a reader must either remember what they learned from reading the implementation last time, or read it again. Fourteen call sites, each paying the same cognitive tax.
A method named reserveInventoryForOrder costs nothing at the call site. The reader knows what it does. The concept is compressed into the name. No implementation reading required. No question held in working memory.
The cognitive cost difference is multiplicative. A method called from 14 sites with a vague name imposes 14 units of cognitive tax. A method called from 14 sites with a precise name imposes zero.
The Before
// HARD TO READ: "processOrder" resists a single verb because it performs six operations.
// SonarQube cognitive complexity: 19. The name is the first symptom.
public Order processOrder(OrderRequest request) {
// Operation 1: Validate address
Address validated = addressValidator.validate(request.getShippingAddress());
if (!validated.isValid()) {
throw new InvalidAddressException(validated.getErrors());
}
// Operation 2: Check inventory
for (OrderLine line : request.getLines()) {
InventoryStatus status = inventoryService.checkAvailability(
line.getProductId(), line.getQuantity());
if (!status.isAvailable()) {
throw new InsufficientInventoryException(
line.getProductId(), line.getQuantity(), status.getAvailable());
}
}
// Operation 3: Reserve stock
List<InventoryReservation> reservations = new ArrayList<>();
for (OrderLine line : request.getLines()) {
InventoryReservation reservation = inventoryService.reserve(
line.getProductId(), line.getQuantity());
reservations.add(reservation);
}
// Operation 4: Calculate rate
ShipmentRate rate = rateCalculator.calculate(
request.getShippingAddress(), request.getLines());
// Operation 5: Select carrier
Carrier carrier = carrierSelector.select(
rate.getServiceLevel(), request.getShippingAddress());
// Operation 6: Create order and shipment
Order order = new Order();
order.setCustomerId(request.getCustomerId());
order.setLines(request.getLines());
order.setShippingAddress(validated);
order.setRate(rate);
order.setCarrier(carrier);
order.setReservations(reservations);
order.setStatus(OrderStatus.PENDING);
return orderRepository.save(order);
}
The comments (“Operation 1,” “Operation 2”) are the developer admitting that the method does multiple things. If the operations were separate methods, the comments would be unnecessary. The comments exist because the name cannot describe the method’s scope. Comments compensating for bad names are a code smell, not good documentation.
The Fix
// READABLE: Each method has an obvious name because each method does one thing.
// The orchestrator reads like a workflow description.
// Cognitive complexity of the orchestrator: 1.
public Order placeOrder(OrderRequest request) {
ValidatedAddress address = validateShippingAddress(request.shippingAddress());
List<StockReservation> reservations = reserveInventory(request.lines());
ShippingQuote quote = quoteShipping(address, request.lines());
return createPendingOrder(request, address, reservations, quote);
}
private ValidatedAddress validateShippingAddress(Address address) {
ValidatedAddress validated = addressValidator.validate(address);
if (!validated.isValid()) {
throw new InvalidAddressException(validated.errors());
}
return validated;
}
private List<StockReservation> reserveInventory(List<OrderLine> lines) {
lines.forEach(line -> inventoryChecker.ensureAvailable(
line.productId(), line.quantity()));
return lines.stream()
.map(line -> inventory.reserve(line.productId(), line.quantity()))
.toList();
}
private ShippingQuote quoteShipping(ValidatedAddress destination,
List<OrderLine> lines) {
ShipmentRate rate = rateCalculator.calculate(destination, lines);
Carrier carrier = carrierSelector.selectFor(rate.serviceLevel(), destination);
return new ShippingQuote(rate, carrier);
}
private Order createPendingOrder(OrderRequest request,
ValidatedAddress address,
List<StockReservation> reservations,
ShippingQuote quote) {
return orderRepository.save(Order.pending(
request.customerId(), request.lines(),
address, reservations, quote));
}
The orchestrator method is now named placeOrder. That name was not available before because the original method included validation, inventory checking, and rate calculation, which are not part of “placing” an order conceptually. They are prerequisites. Once extracted, the orchestrator’s name becomes obvious: it places an order, given that the prerequisites are met.
Each extracted method has a name that required no deliberation. validateShippingAddress, reserveInventory, quoteShipping, createPendingOrder. The names were hard when the operations were combined. They were obvious when the operations were separated. That is the diagnostic: if splitting a method makes both halves trivially nameable, the original method was doing too many things.
The Rule
If a method resists a name more specific than “process,” “handle,” or “manage,” split it until each piece has an obvious name. The naming difficulty is the diagnostic. Do not solve it with a thesaurus. Solve it with decomposition. The right name for each piece will be obvious once the piece does only one thing. If a method has comments labeling its internal sections (“Step 1,” “Validate,” “Calculate”), each section should be its own method with a name that replaces the comment.