PushBackLog

Code Smells

Advisory enforcement Complete by PushBackLog team
Topic: clean-code Topic: quality Skillset: backend Skillset: frontend Skillset: fullstack Technology: generic Stage: execution Stage: review

Code Smells

Status: Complete
Category: Clean Code
Default enforcement: Advisory
Author: PushBackLog team


Tags

  • Topic: clean-code, quality
  • Skillset: backend, frontend, fullstack
  • Technology: generic
  • Stage: execution, review

Summary

Code smells are surface-level symptoms in source code that indicate deeper structural problems. They are not bugs — the code may be correct — but they signal that the design is working against the engineer, increasing the cost of change, adding cognitive load, and creating brittle seams where future changes are likely to introduce defects. Recognising and naming code smells is the first step toward systematic refactoring.


Rationale

Smells as diagnostic signals

Martin Fowler and Kent Beck’s taxonomy of code smells (from Refactoring) provides a shared vocabulary for discussing code quality. Without named smells, engineers either say vague things like “this feels messy” or say nothing at all. With named smells, a reviewer can say “this is a Feature Envy — calculateDiscount is reaching into three Customer fields and would better belong on Customer” and propose a concrete, targeted refactoring.

Technical debt lives in smells

Smells are how technical debt becomes visible before it becomes a maintenance crisis. A codebase that accumulates smells without remediation becomes increasingly expensive to change — not because any single smell is catastrophic, but because they compound. Long methods make it hard to test anything in isolation. Divergent changes mean one requirement touches eight files. Data clumps mean the same three parameters appear in fifteen function signatures. The combination is a codebase where engineers dread touching anything because the blast radius of every change is large and unpredictable.


Guidance

Common code smells

Bloaters — things that have grown too large

SmellDescriptionRefactoring
Long MethodFunction too long to understand at a glance (> 20 lines is a heuristic)Extract Method
Large ClassClass with too many responsibilities, fields, or methodsExtract Class, Extract Subclass
Long Parameter ListFunction with four or more parametersIntroduce Parameter Object, Preserve Whole Object
Data ClumpsThe same two or three values always appear togetherExtract Class into a Value Object
Primitive ObsessionUsing primitives instead of small objects for domain concepts (money as number, status as string)Replace Primitive with Object

Object-orientation abusers — misuse of OO constructs

SmellDescriptionRefactoring
Switch StatementsSame switch or if-else chain appearing in multiple placesReplace Conditional with Polymorphism
Temporary FieldInstance variable only set under certain conditionsIntroduce Null Object, Extract Class
Refused BequestSubclass inherits parent interface but only uses part of itReplace Inheritance with Delegation
Alternative Classes with Different InterfacesTwo classes doing the same thing with different method namesRename Method, Extract Superclass

Change preventers — changes that ripple unexpectedly

SmellDescriptionRefactoring
Divergent ChangeOne class changes for many different reasonsExtract Class per reason-to-change
Shotgun SurgeryOne change requires small edits scattered across many classesMove Method, Move Field
Parallel Inheritance HierarchiesAdding a subclass in one hierarchy forces a new subclass in anotherMove Method, Move Field

Couplers — excessive interaction between classes

SmellDescriptionRefactoring
Feature EnvyA method that uses more data from another class than its ownMove Method
Inappropriate IntimacyTwo classes know too much about each other’s internalsMove Method, Move Field, Change Bidirectional to Unidirectional
Message Chainsa.getB().getC().getD().doThing() — the Law of Demeter violatedExtract Method, Hide Delegate
Middle ManA class that does nothing but delegate to anotherInline Method, Remove Middle Man

Dispensables — things that should not exist

SmellDescriptionRefactoring
CommentsComments that exist to explain what unclear code does (not why)Extract Method with a good name
Duplicate CodeSame or similar logic in multiple placesExtract Method, Pull Up Method
Lazy ClassA class that does too little to justify existingInline Class, Collapse Hierarchy
Data ClassA class with only getters/setters and no behaviourMove Method into the class
Dead CodeUnreachable or unused codeDelete it
Speculative GeneralityAbstraction created for hypothetical future requirements (YAGNI violation)Inline Class, Remove Parameter

Using smells in code review

Code review comments citing a named smell are more actionable and less personal than vague quality feedback:

# Too vague
"This method feels too big"

# More actionable
"This looks like a Long Method — the payment processing and notification logic
could be extracted into `ChargeCard` and `NotifyOrderPlaced` respectively,
with the calling method reading like a summary."

Name the smell, describe the specific instance, and suggest the refactoring.

Metrics that surface smells

  • Cyclomatic complexity (> 10 in a single function is a smell) — detects complex branching (Long Method, Switch Statements)
  • Lines of code per function (> 30 is a heuristic) — detects Long Method
  • Number of parameters (> 3) — detects Long Parameter List
  • Class coupling — detects Feature Envy, Inappropriate Intimacy
  • Duplication rate — detects Duplicate Code

Most linting or static analysis tools can be configured to report these automatically as part of CI.