Long Method, Large Class, Feature Envy, Data Clumps
What This Concept Is
Fowler groups code smells into families. Two of the loudest families live at the method and class level: bloaters (things that grew too large) and couplers (things that reach across boundaries they should respect). Four smells cover most of what you will see in a code review:
- Long Method. A function that keeps going, usually because nobody paused to name its phases. Every additional line reduces comprehension per line read.
- Large Class. A class with too many fields and methods, usually because it absorbed responsibilities one at a time. It is what a Single Responsibility violation looks like at the file level.
- Feature Envy. A method that spends more time using another object's data than its own. It belongs somewhere else.
- Data Clumps. The same three or four data items travel together across functions, classes, and method signatures without a home.
Each is a diagnostic pattern paired with a canonical refactoring move.
Why It Matters Here
These four smells produce the majority of design complaints in real-world review. Naming them sharpens otherwise-vague feedback ("this is messy" -> "this method is doing three levels and should be split into validate, compute, and render").
They also form a refactoring ladder: spot a Long Method, extract functions, discover a Data Clump inside, extract a class, and the Large Class starts to shrink. One smell leads to the next move.
Concrete Example
A small block with all four smells at once:
class OrderService {
void process(Order o) { // Long Method + entry point
// validation
if (o.street == null || o.city == null || o.zip == null) throw ... // Data Clump
// tax
double taxRate = TaxRules.rateFor(o.country, o.state, o.zip); // Feature Envy on TaxRules
double tax = o.subtotal * taxRate;
// email
emailer.send("Order #" + o.id + " for " + o.customerName + " total " + (o.subtotal + tax));
// ... 70 more lines
}
}
processis a Long Method: validation, tax, and notification interleaved.OrderServiceis turning into a Large Class: every order concern lives here.- The tax section reads more
TaxRulesdata than it uses fromOrder-- Feature Envy. street / city / zipalways travel together -- a Data Clump crying out to be anAddress.
Refactored sketch:
class Address { /* street, city, zip, validate() */ } // Extract Class (removes Data Clump)
class TaxCalculator { double of(Order o) { /* ... */ } } // Move Function (fixes Feature Envy)
class OrderNotifier { void notify(Order o, double tax) { /* ... */ } }
class OrderService {
void process(Order o) { // now a four-line plan
o.address.validate();
double tax = taxCalculator.of(o);
orderNotifier.notify(o, tax);
}
}
The long method became a short plan. Each extracted piece has one reason to change.
Common Confusion / Misconception
"Long Method is about line count." Line count is the symptom, not the problem. The real issue is mixed levels of abstraction (see Cluster 3). A 60-line pure function describing one coherent calculation is not a Long Method in the harmful sense.
A second confusion: "Feature Envy means the envious method is wrong." Sometimes it signals the method is in the wrong class. But sometimes the "envied" class is a Data Clump that has not yet been given its own methods -- Feature Envy disappears when you create the missing object.
A third: "Large Class is fixed by splitting the file." Splitting the file without splitting the responsibility is just hiding the problem in two places.
How To Use It
When you spot one of these smells, follow the canonical move:
- Long Method -> Extract Function. Name each step. Move each step below the caller.
- Large Class -> Extract Class. Group methods and fields that operate on the same data; move them out.
- Feature Envy -> Move Function. If the method uses more of
B's data thanA's, move it toB. - Data Clumps -> Extract Class / Introduce Parameter Object. Wrap the recurring group in a named type.
Check Yourself
- Why does Feature Envy sometimes mean the envied class is missing methods rather than that the envious method is in the wrong place?
- Why is "line count" the wrong primary metric for Long Method?
- How does Data Clump removal usually reduce Feature Envy elsewhere?
Mini Drill or Application
Pick a class over 200 lines in code you know. Do all four:
- Find any method over 20 lines. Name each of its phases; mark extract candidates.
- Find any group of three or more parameters that appear together in two or more methods -- that is a Data Clump.
- Find any method that calls more getters on another object than it uses of its own fields -- that is Feature Envy.
- Sketch the target design after one
Extract Classand oneMove Function.
Read This Only If Stuck
- Refactoring: Long Function to Long Parameter List
- Refactoring: Divergent Change to Feature Envy
- Refactoring: Extract Function (Part 1)
- Refactoring: Replace Temp with Query and Extract Class
- Refactoring: Rename variable and Introduce Parameter Object
- Good Code, Bad Code: Caring too much about other classes