Code Smells Catalog
Code smells are surface indications of deeper problems. Like symptoms of an illness, they don't tell you exactly what's wrong, but they point you in the right direction. This article provides a comprehensive catalog of code smells with detection techniques, examples, and recommended refactorings.
📋 At a Glance
| Aspect | Details |
|---|---|
| Total Smells Covered | 22 core smells |
| Categories | Bloaters, OO Abusers, Change Preventers, Dispensables, Couplers |
| Detection Tools | SonarQube, IntelliJ inspections, SpotBugs |
| Key Insight | Smell = Symptom, not disease |
| Business Impact | Early smell detection prevents 60% of major refactorings |
🎯 What You'll Learn
After reading this article, you will be able to:
- Recognize all major code smells at a glance
- Understand the root causes behind each smell
- Apply appropriate refactorings for each smell
- Configure tools for automatic smell detection
- Prioritize smells based on impact and fix difficulty
- Use smells in code reviews constructively
🔥 Production Story: The Thousand-Line Method
A team inherited a legacy e-commerce system. The checkout method was legendary:
JAVA(13 lines)CodeLoading syntax highlighter...
- Bug fixes took 2 days minimum (half day just to understand the code)
- Every change broke something else
- No one could test it properly (integration test took 8 minutes)
- New developers avoided touching it
- Long Method (1,047 lines)
- Feature Envy (reached into 12 different classes)
- Data Clumps (same 5 parameters passed everywhere)
- Comments (100+ comments explaining "what" instead of "why")
- Duplicate Code (discount logic repeated 4 times with variations)
- Extracted 14 focused methods
- Created 6 collaborating services
- Introduced Parameter Objects for data clumps
- Result: Each method < 20 lines, 90% test coverage, bugs fixed in hours
🧠 Mental Model: Smell Categories
TEXT(36 lines)CodeLoading syntax highlighter...
🔬 Deep Dive: Complete Smell Catalog
Category 1: Bloaters
Long Method
- Multiple levels of abstraction in one method
- Comments separating "sections"
- Scrolling required to read method
- Multiple responsibilities
JAVA(64 lines)CodeLoading syntax highlighter...
Large Class (God Class)
- Class name includes "Manager", "Processor", "Handler", "Util"
- Many unrelated methods
- Multiple groups of methods using different fields
- Difficult to name precisely
JAVA(44 lines)CodeLoading syntax highlighter...
Primitive Obsession
- String for email, phone, currency code
- int for quantities, amounts, IDs
- Validation logic scattered everywhere
JAVA(43 lines)CodeLoading syntax highlighter...
Long Parameter List
- Easy to mix up parameter order
- Boolean parameters (what does
truemean?) - Many parameters of same type
JAVA(34 lines)CodeLoading syntax highlighter...
Data Clumps
- Same 3+ parameters passed to multiple methods
- Same fields in multiple classes
- Same local variables declared together
JAVA(31 lines)CodeLoading syntax highlighter...
Category 2: Object-Orientation Abusers
Switch Statements
- Switch on type field
- Same switch repeated in multiple places
- Adding new type requires changes everywhere
JAVA(26 lines)CodeLoading syntax highlighter...
Temporary Field
- Field is null/default most of the time
- Complex conditionals check if field is set
- Field only populated for specific operations
JAVA(35 lines)CodeLoading syntax highlighter...
Refused Bequest
- Overriding methods to throw UnsupportedOperationException
- Empty overrides
- Ignoring inherited fields
JAVA(31 lines)CodeLoading syntax highlighter...
Category 3: Change Preventers
Divergent Change
- "I have to change this class whenever..."
- Different parts of class change for different features
- Multiple developers editing same class for different reasons
JAVA(24 lines)CodeLoading syntax highlighter...
Shotgun Surgery
- Adding a field requires changes in 10 places
- Small feature touches many files
- Fear of making changes due to ripple effects
JAVA(20 lines)CodeLoading syntax highlighter...
Parallel Inheritance Hierarchies
- Every Shape needs a ShapeFactory
- Every Employee needs an EmployeeUI
- Hierarchies mirror each other
JAVA(30 lines)CodeLoading syntax highlighter...
Category 4: Dispensables
Comments (Excessive)
- Every line has a comment
- Comments repeat what code says
- Comments become outdated
- Comments apologize for bad code
JAVA(27 lines)CodeLoading syntax highlighter...
Duplicate Code
- Copy-paste patterns
- Bug fixed in one place but not another
- Same structure with slight variations
JAVA(36 lines)CodeLoading syntax highlighter...
Speculative Generality
- Abstract class with only one subclass
- Parameters/options that are never used
- Complex configuration for simple needs
- "We might need this someday"
JAVA(21 lines)CodeLoading syntax highlighter...
Category 5: Couplers
Feature Envy
- Method calls many getters on another object
- Method manipulates another object's data extensively
- Logic should probably live in the other class
JAVA(37 lines)CodeLoading syntax highlighter...
Inappropriate Intimacy
- Classes access each other's private data (via reflection or package-private)
- Bi-directional dependencies
- Changes in one class break the other
JAVA(37 lines)CodeLoading syntax highlighter...
Message Chains
a.getB().getC().getD().doSomething()- Violation of Law of Demeter
- Tight coupling to object structure
JAVA(27 lines)CodeLoading syntax highlighter...
Middle Man
- Most methods just call same method on another object
- Class adds no value
- Extra indirection without purpose
JAVA(46 lines)CodeLoading syntax highlighter...
📊 Smell Detection Tools Configuration
SonarQube Rules
YAML(13 lines)CodeLoading syntax highlighter...
IntelliJ Inspections
TEXT(16 lines)CodeLoading syntax highlighter...
⚠️ Common Mistakes
Mistake 1: Treating Smells as Absolutes
TEXT(4 lines)CodeLoading syntax highlighter...
Mistake 2: Refactoring Without Tests
TEXT(3 lines)CodeLoading syntax highlighter...
Mistake 3: Over-Refactoring
TEXT(4 lines)CodeLoading syntax highlighter...
🐛 Debug This: The Mystery of the Failing Refactoring
A developer reports: "I refactored this method to be shorter and now it doesn't work. But I only extracted methods - I didn't change any logic!"
JAVA(54 lines)CodeLoading syntax highlighter...
- Original problem:
currentOrderis a temporary field - it's only valid duringprocessOrder() - Refactoring issue: The new code uses
parallelStream(), which executes lambdas in different threads - Race condition: By the time some parallel steps run,
currentOrdermight already be null
- Temporary Field:
currentOrderis set, used, then cleared - class state is inconsistent - Mutable Shared State: Field is modified during execution
- Hidden Coupling: Methods implicitly depend on
currentOrderbeing set
order as parameter to each method instead of storing in field.💻 Exercises
Exercise 1: Smell Identification
⭐ Difficulty: Easy | ⏱️ Time: 15 minutes
JAVA(33 lines)CodeLoading syntax highlighter...
| Smell | Location | Severity |
|---|---|---|
| Long Parameter List | 6 parameters | High |
| Long Method | 100+ lines implied | High |
| Switch Statements | type-based switch | Medium |
| Temporary Field | tempReport, isProcessing | Medium |
| Primitive Obsession | String type, String format | Low |
Exercise 2: Refactoring Practice
⭐⭐ Difficulty: Medium | ⏱️ Time: 20 minutes
JAVA(18 lines)CodeLoading syntax highlighter...
JAVA(33 lines)CodeLoading syntax highlighter...
Exercise 3: Code Smell Catalog
⭐⭐ Difficulty: Medium | ⏱️ Time: 15 minutes
| Smell | Solution |
|---|---|
| 1. Long Method | A. Replace with Strategy/Polymorphism |
| 2. Long Parameter List | B. Extract Class |
| 3. Data Clumps | C. Introduce Parameter Object |
| 4. Switch Statements | D. Extract Method |
| 5. Divergent Change | E. Move Method to appropriate class |
| 6. Feature Envy | F. Create dedicated class for clumped data |
| Smell | Solution |
|---|---|
| Long Method | D. Extract Method |
| Long Parameter List | C. Introduce Parameter Object |
| Data Clumps | F. Create dedicated class for clumped data |
| Switch Statements | A. Replace with Strategy/Polymorphism |
| Divergent Change | B. Extract Class |
| Feature Envy | E. Move Method to appropriate class |
Exercise 4: Refactor Data Clumps
⭐⭐⭐ Difficulty: Medium-Hard | ⏱️ Time: 20 minutes
JAVA(12 lines)CodeLoading syntax highlighter...
JAVA(20 lines)CodeLoading syntax highlighter...
Exercise 5: Complete Smell Audit
⭐⭐⭐⭐ Difficulty: Hard | ⏱️ Time: 25 minutes
JAVA(35 lines)CodeLoading syntax highlighter...
Smells found:
- Temporary Fields:
initialized,conn,lastQuery - Long Parameter List: 7 parameters
- Switch Statements: type-based branching
- Long Method: 100+ lines implied
- Inappropriate Intimacy: Direct DB connection handling
- Feature Envy: Email sending mixed with reporting
JAVA(29 lines)CodeLoading syntax highlighter...
🎤 Senior-Level Interview Questions
Question #1: How do you prioritize which smells to fix?
I use a risk-based approach:
-
High priority:
- Smells in hot paths (frequently changed code)
- Smells blocking new features
- Smells causing production bugs
-
Medium priority:
- Smells in moderately changed code
- Smells slowing down development
-
Low priority:
- Smells in stable, rarely-touched code
- Cosmetic smells in working code
Priority = (Change Frequency × Bug Density × Business Impact)Question #2: What's the relationship between smells and SOLID?
Smells are symptoms of SOLID violations:
| Smell | Usually Violates |
|---|---|
| Long Method | SRP |
| Large Class | SRP |
| Divergent Change | SRP |
| Shotgun Surgery | SRP (inverted) |
| Switch Statements | OCP |
| Refused Bequest | LSP |
| Fat Interface smell | ISP |
| Feature Envy | SRP, encapsulation |
Knowing SOLID helps predict what refactoring to apply.
Question #3: When should you NOT fix a code smell?
- Code is being deleted soon - waste of effort
- No test coverage - risk of breaking it
- Deadline pressure - log it, fix after
- Code is stable and working - "ain't broke"
- Smell is intentional (trade-off) - document why
- Fix creates worse smell - cure worse than disease
📝 Summary & Key Takeaways
Quick Reference Table
| Category | Smell | Refactoring |
|---|---|---|
| Bloaters | Long Method | Extract Method |
| Bloaters | Large Class | Extract Class |
| Bloaters | Primitive Obsession | Value Object |
| Bloaters | Long Parameter List | Parameter Object |
| OO Abusers | Switch Statements | Polymorphism |
| OO Abusers | Refused Bequest | Composition |
| Change Preventers | Divergent Change | Extract Class |
| Change Preventers | Shotgun Surgery | Move Method |
| Dispensables | Duplicate Code | Extract Method |
| Dispensables | Comments | Self-documenting code |
| Couplers | Feature Envy | Move Method |
| Couplers | Message Chains | Hide Delegate |
Detection Checklist
- Method > 20 lines?
- Class > 200 lines?
- Parameters > 4?
- Switch on type?
- Same code in multiple places?
- Method uses other class's data extensively?
- Long chains of getters?
🏁 Conclusion
Code smells are your early warning system. They tell you where problems are brewing before they become crises. Learn to recognize them, and you'll catch issues while they're still easy to fix.
📅 Review Schedule for This Article
| Day | Task | Time |
|---|---|---|
| Day 1 | Review Code Smells Catalog table | 5 min |
| Day 3 | Redo Exercise 1 (Smell Identification) | 15 min |
| Day 7 | Answer interview questions without looking | 10 min |
| Day 14 | Redo Debug This (Temporary Field paradox) | 10 min |
| Day 30 | Audit one class in your project for code smells | 15 min |