Refactoring & Code Quality

Code Smells

A smell is a surface symptom pointing at a deeper design problem — not a bug, and not always wrong, but a signal worth investigating. Naming them gives a team shared vocabulary for "something here is making change harder than it should be."
  • A smell doesn't mean the code is incorrect — it means it resists change, which is where the real cost shows up
  • Duplicated Code is the most fundamental smell — nearly every other smell either causes it or is caused by it
  • Long Method / Large Class: size alone isn't the problem, low cohesion inside that size is
  • Feature Envy: a method more interested in another class's data than its own — a sign it's living in the wrong place
  • Divergent Change (one class changes for many unrelated reasons) and Shotgun Surgery (one reason to change touches many classes) are mirror-image cohesion problems
  • Smells guide which refactoring to reach for — see Refactoring Catalog Core for the mechanics
Common smells and their usual fix
SmellWhat it looks likeLikely refactoring
Duplicated CodeSame expression/structure copy-pastedExtract Method, then call it from both places
Long MethodA method that needs scrolling to readExtract Method on cohesive chunks
Long Parameter ListFour, five, six positional parametersIntroduce Parameter Object
Feature EnvyMethod reaches into another object's data repeatedlyMove Method to the object it envies
Data ClumpsThe same 3–4 fields always travel together as parametersExtract a class for the clump
Primitive ObsessionA String/int standing in for a real concept (money, email)Replace primitive with a small value type
Switch StatementsThe same type-code switch repeated at several call sitesReplace Conditional with Polymorphism

Smells are heuristics, not indictments — a 40-line method that reads top-to-bottom as one clear narrative is not automatically a "Long Method" problem; a Utils class gathering three unrelated one-line helpers might be a pragmatic trade-off, not a crisis. The judgment call smells demand is: does this shape make the next change harder than it needs to be? If the answer is no, leave it — refactoring for its own sake, on code nobody is touching, is pure cost with no payoff (see Technical Debt on when paying down debt is actually worth it).

Feature Envy — the method is jealous of the wrong class
// Envious: OrderPrinter cares more about Order's internals than its own.
class OrderPrinter {
    String describe(Order order) {
        return order.getCustomerName() + " ordered " + order.getItems().size()
             + " items totaling 
quot;
+ order.getItems().stream().mapToDouble(Item::getPrice).sum(); } } // Fixed: the logic moves to live with the data it uses (Move Method). class Order { String describe() { return customerName + " ordered " + items.size() + " items totaling
quot;
+ items.stream().mapToDouble(Item::getPrice).sum(); } }
Sources
  • Refactoring: Improving the Design of Existing Code (2nd ed.)Ch. 3 — Bad Smells in Code