Overview
Code smells are surface symptoms of deeper design problems: long methods, large classes, duplicated logic, long parameter lists, feature envy (a method more interested in another class's data), switch statements on type, primitive obsession, and shotgun surgery (one change requires edits in many places). Refactoring improves structure without changing behavior, in small, test-backed steps.
Knowing the smells and their standard refactorings (Extract Method, Extract Class, Move Method, Replace Conditional with Polymorphism, Introduce Parameter Object, Replace Primitive with Value Object) lets you both write better LLD solutions and discuss how you would improve an existing design, a frequent interview follow-up.
You do not throw everything out; you move items into labeled shelves one box at a time, checking you can still find things. Afterward, finding and adding tools is much easier.
When to use it
- Before adding a feature to messy code.
- Code reviews and interview follow-ups ('what would you improve?').
- When a change touches many unrelated places.
Where it shows up in interviews
Recognize it when: 'here is some code, how would you improve it?'
- Refactor a god class OrderManager
- Refactor a switch-based pricing function
Where it is used in real software
The catalog of smells and refactorings used across the industry.
IntelliJ, VS Code, and Rider automate extract method, rename, and move safely.
SonarQube and ESLint flag complexity, duplication, and long methods.
Key terms
- Refactoring
- Behavior-preserving structural change.
- Long method / large class
- Too much in one place.
- Feature envy
- A method using another object's data more than its own.
- Shotgun surgery
- One change requires many small edits across classes.
- Primitive obsession
- Primitives instead of small domain types.
How it works, step by step
- 1Get tests around the behavior
Characterization tests if none exist.
- 2Identify the smell
Name it precisely.
- 3Apply the matching refactoring
Small step, run tests.
- 4Repeat
Commit frequently.
- 5Stop when the change you need is easy
Refactor with purpose.
Smells and fixes
Common smells in LLD code
| Smell | Symptom | Refactoring |
|---|---|---|
| Long method | 200-line checkout() | Extract Method into named steps |
| Large class | OrderManager does pricing, email, DB | Extract Class (SRP) |
| Switch on type | if vehicle.type == TRUCK ... | Replace Conditional with Polymorphism |
| Long parameter list | book(a, b, c, d, e, f) | Introduce Parameter Object |
| Feature envy | Invoice computes from customer fields | Move Method to Customer |
| Primitive obsession | double price, String email | Replace Primitive with Value Object |
NOWSmell: Long method | Symptom: 200-line checkout() | Refactoring: Extract Method into named steps
Each smell has a well-known, low-risk fix.
Implementation
// Smells: long method, switch on type, primitives, mixed concernsfunction checkout(items: any[], type: string, email: string, coupon: string) { let total = 0; for (const i of items) total += i.price * i.qty; if (type === "gold") total *= 0.8; else if (type === "silver") total *= 0.9; if (coupon === "SAVE10") total -= 10; if (total < 0) total = 0; db.save({ items, total }); mailer.send(email, "Thanks! Total " + total.toFixed(2)); return total;}Complexity and performance
Small and safe.
High without.
Trade-offs
Refactoring before a change makes the change easier; refactoring code nobody touches has low value.
Rewrites are risky; incremental refactoring keeps the system working.
Variants and related techniques
Incrementally replace a legacy module behind an interface.
'Make the change easy, then make the easy change.'
Common mistakes
- Refactoring without tests.
Fix: Add characterization tests first.
- Mixing refactoring with behavior changes.
Fix: Separate commits: structure first, then feature.
Interview questions
What refactoring would you apply to a large switch on object type?
Replace Conditional with Polymorphism: introduce an interface, move each branch into a class implementing it, and let callers call the method without checking types.
What is shotgun surgery and how do you fix it?
When one logical change requires edits in many classes. Fix by moving the related logic into one place (Move Method/Field, Extract Class) so the concept has a single home.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Refactor a 150-line OrderManager | Medium | Extract Class and Method. |
| Replace a vehicle-type switch with polymorphism | Easy | Conditional to polymorphism. |