Antipatterns Overview
Antipatterns are common solutions to common problems where the solution is ineffective and may result in flawed or counterproductive results.
Table of Contents
Bloaters
Code, methods, and classes that have grown to massive proportions.
- Long Method: A method that has grown too large.
- Large Class: A class that does too much.
- Primitive Obsession: Using primitives instead of small objects for simple tasks.
- Long Parameter List: More than three or four parameters for a method.
- Data Clumps: Bunches of data that hang around together.
- Spaghetti Code: Unstructured control flow.
Object-Orientation Abusers
Incomplete or incorrect application of OO principles.
- Switch Statements: Complex switch operators or sequences of if statements.
- Temporary Field: Fields only used in specific circumstances/methods.
- Refused Bequest: Subclasses that don’t use the inherited methods/data.
- Alternative Classes with Different Interfaces: Two classes do the same thing but have different signatures.
- Anemic Domain Model: Domain objects with no behavior.
Change Preventers
Structural issues that make modification difficult.
- Divergent Change: One class changed in different ways for different reasons.
- Shotgun Surgery: One change requires many small edits across many classes.
- Parallel Inheritance Hierarchies: Subclassing one class forces subclassing another.
Couplers
Excessive coupling between classes.
- Feature Envy: A method uses another class’s data more than its own.
- Inappropriate Intimacy: Classes that know too much about each other’s internals.
- Message Chains: Long chains of object navigation.
- Middle Man: A class that only delegates.
Dispensables
Pointless or unneeded code.
- Comments: Comments that explain “what” or “how” instead of “why”.
- Duplicate Code: Identical or very similar code exists in more than one location.
- Lazy Class: A class that doesn’t do enough to justify existence.
- Data Class: Classes with fields, getters/setters, and nothing else.
- Dead Code: Unused variables, params, methods, files.
- Speculative Generality: Building for future requirements that may never come.
- Boat Anchor: Unused code kept “just in case”.
Implementation & Cognitive Load
Smells that make code hard to read or reason about.
- Magic Numbers/Strings: Unexplained literals instead of named constants.
- Arrow Code: Excessive indentation levels.
- Uncommunicative Name: Names that don’t reveal intent.
- Inconsistent Names: Different names for the same concept.
- Hidden Side Effects: Function does more than it promises.
- Flag Arguments: Boolean arguments that split function behavior.
- Cargo Cult Programming: Copying code without understanding.
- Premature Optimization: Optimizing before it’s needed.
- Golden Hammer: Misapplying familiar technology to everything.
Stability & Reliability
Patterns that lead to bugs.
- Improper Error Handling: Empty catch blocks, swallowing exceptions.
- Null Check Hell: Excessive null checks.
- Race Conditions: Unsafe shared mutable state.
- Hardcoded Configuration: Storing config in codebase.
Architecture Smells
Design issues at the system level.
- Cyclic Dependency: Cycles in the component dependency graph.
- Hub-like Dependency: A component with too many dependencies.
- Leaking Abstraction: Implementation details bubbling up.
- Lava Flow: Dead code accumulating at project level.
- Poltergeists: Short-lived, useless controller classes.
Unit Test Smells
Issues in test code.
- Fragile Test: Fails due to unrelated changes.
- Obscure Test: Hard to understand verification logic.
- Test Logic in Production: Production code contains testing hooks.
- Assertion Roulette: Multiple assertions without messages.
- Slow Tests: Slow tests won’t be run.
- Mystery Guest: Test relies on external resources.
- General Fixture: Setup is too big/general.