Mesrai
All guides
Learn · Code smells

Code smells: meaning, a full list with examples, and how to fix them

Updated · Mesrai editorial team

Short answer

A code smell is a characteristic of code that suggests a deeper design problem, even though the code still works — for example a very long function, duplicated logic, or a class that reaches into another class's data. Code smells are not bugs, but they make code harder to understand and change, and they make future bugs more likely. The usual fix is a small, targeted refactoring, such as extracting a function or moving a method to the data it uses.

What does 'code smell' mean?

Kent Beck coined the term in the late 1990s, and Martin Fowler made it widely known in Refactoring (1999), which pairs each smell with refactorings that remove it. The metaphor is deliberate: a smell is a hint to look closer, not proof that something is wrong. A long function may be perfectly fine; the smell is the prompt to ask whether it should be split.

Smells matter because software spends most of its life being changed. Code that is hard to read takes longer to change safely, attracts copy-paste fixes, and hides bugs from reviewers. Removing smells in small steps keeps that cost from compounding.

Types of code smells: the five categories

Fowler's original list has been grouped into five categories, following a taxonomy proposed by Mäntylä and colleagues in 2003. The categories make smells easier to recognise because each group has a shared cause.

CategoryWhat goes wrongSmells in this group
BloatersCode grows too large to work withLong Method, Large Class, Primitive Obsession, Long Parameter List, Data Clumps
Object-orientation abusersOO features used incompletely or wronglySwitch Statements, Temporary Field, Refused Bequest, Alternative Classes with Different Interfaces
Change preventersOne change forces many othersDivergent Change, Shotgun Surgery, Parallel Inheritance Hierarchies
DispensablesSomething that isn't neededDuplicate Code, Dead Code, Lazy Class, Data Class, Speculative Generality, Comments that explain unclear code
CouplersClasses too tightly linkedFeature Envy, Inappropriate Intimacy, Message Chains, Middle Man

List of code smells with examples and fixes

The most common smells, what they look like, and the refactoring that usually removes them:

  • Long Method. A function that does several things. Fix: Extract Function for each step, named after what it does.
  • Large Class (God Object). A class with too many fields and responsibilities. Fix: Extract Class along its responsibilities.
  • Primitive Obsession. Strings and numbers used for domain concepts such as money, emails or IDs. Fix: small value types that validate themselves.
  • Long Parameter List. Many arguments, easily passed in the wrong order. Fix: Introduce Parameter Object.
  • Data Clumps. The same values always travel together (start, end, timezone). Fix: make them one type.
  • Switch Statements. The same switch on a type repeated in several places. Fix: polymorphism or a lookup table.
  • Temporary Field. A field that is only set in some situations and empty otherwise. Fix: move it into its own object.
  • Refused Bequest. A subclass that ignores most of what it inherits. Fix: replace inheritance with composition.
  • Divergent Change. One module changes for many unrelated reasons. Fix: split it so each part has one reason to change.
  • Shotgun Surgery. One change requires edits in many files. Fix: Move Function / Move Field to gather the logic.
  • Duplicate Code. The same logic in several places, drifting apart. Fix: extract a shared function — once the copies are truly the same concept.
  • Dead Code. Functions, flags and branches nothing uses. Fix: delete them; version control remembers.
  • Lazy Class. A class that does too little to justify existing. Fix: inline it.
  • Data Class. A class with fields and no behaviour, while other code manipulates its data. Fix: move that behaviour into the class.
  • Speculative Generality. Abstractions built for needs that never arrived. Fix: inline them until a second real use appears.
  • Feature Envy. A method that uses another object's data more than its own. Fix: move the method to that data.
  • Inappropriate Intimacy. Classes reaching into each other's internals. Fix: move methods and fields, or hide internals behind an interface.
  • Message Chains. Calls like order.customer().address().city(). Fix: Hide Delegate — ask for what you need.
  • Middle Man. A class that only forwards calls. Fix: remove it and call the real object.

Example: Primitive Obsession with money

Representing money as a floating-point number is a classic smell that turns into real bugs: rounding errors and mixed currencies.

Smell: money as a float, currency implied
function applyDiscount(price: number, percent: number) {
  return price - price * (percent / 100); // rounding drift on some amounts
}

// Better: integer minor units with an explicit currency
type Money = { amountMinor: number; currency: "INR" | "USD" };

function applyDiscount(price: Money, percent: number): Money {
  const off = Math.round((price.amountMinor * percent) / 100);
  return { ...price, amountMinor: price.amountMinor - off };
}

Python code smells

Every language has smells that come from its own features. Python's most common ones include mutable default arguments, bare except clauses that swallow every error, wildcard imports that hide where names come from, deeply nested conditionals, and 'god modules' that collect unrelated helpers.

Two common Python smells and their fixes
# Smell: mutable default argument — the list is shared between calls
def add_tag(tag, tags=[]):
    tags.append(tag)
    return tags

def add_tag(tag, tags=None):
    tags = [] if tags is None else tags
    tags.append(tag)
    return tags

# Smell: bare except hides every failure, including KeyboardInterrupt
try:
    charge(order)
except:
    pass

try:
    charge(order)
except PaymentDeclined as e:
    log.warning("payment declined", extra={"order_id": order.id, "reason": e.code})

Code smells in SonarQube

SonarQube has long used 'code smell' as its name for maintainability issues — alongside bugs (reliability) and vulnerabilities (security). Its rules flag measurable smells such as overly complex functions (cognitive complexity), duplicated blocks, long parameter lists, unused variables and commented-out code, and its technical-debt estimate is largely built from them. Newer versions group findings by software quality — maintainability, reliability and security — but the idea is the same.

Rule-based tools are good at smells that can be measured. Design smells such as Feature Envy or Shotgun Surgery depend on what the code means, so they are usually found in code review.

Code smells vs bugs vs anti-patterns

A bug is code that produces the wrong result. A code smell is code that works today but is hard to understand or change safely. An anti-pattern is a common, named solution that tends to cause more problems than it solves; some smells, such as the God Object, are also anti-patterns.

The distinction matters in review: a bug blocks the merge, while a smell is usually a suggestion — unless the change makes an existing smell significantly worse.

How to find code smells

  • Linters and static analysis. Measurable smells: function length, complexity, duplication, unused code (ESLint, Pylint, SonarQube, PMD).
  • Code review. Design smells such as Feature Envy and Shotgun Surgery, which need an understanding of the domain.
  • AI code review. Smells in context — for example a new helper that duplicates one that already exists elsewhere in the repository — with the suggested refactoring.
  • Change history. Files that change in almost every pull request, or always change together, often hide Shotgun Surgery or Divergent Change.

When to fix a code smell — and when not to

Fix smells in code you are already changing — the 'leave it a little cleaner' rule — and only when tests cover the behaviour. Avoid large refactors mixed into feature pull requests: they make review harder and hide behaviour changes. If a smell needs a big change, do it in its own pull request.

Leave smells in stable code nobody touches. A smell only costs something when someone has to read or change the code.

Code smell review checklist

  • New functions do one thing and fit on a screen.
  • The change doesn't copy logic that already exists elsewhere.
  • Functions take few parameters; related values travel as one type.
  • Domain values such as money, IDs and emails have their own types.
  • No new switch-on-type chains duplicating existing ones.
  • Methods live with the data they use.
  • Unused code, flags and parameters introduced by the change are removed.
  • Refactoring is in a separate pull request from behaviour changes.
// mesrai

How Mesrai helps with code smells

Mesrai flags maintainability problems in the context of your repository — such as a new helper that duplicates one that already exists, or money handled as a float — and suggests the refactoring. Severity filters keep smells as low-priority suggestions so they never drown out real bugs.

  • Smells flagged with the suggested refactoring
  • Repository graph spots duplication across files
  • Severity filters keep suggestions separate from blocking issues
  • Your own style rules as Mesrai Rules

Sources

Further reading

?

Code smells — frequently asked

7 questions
  • What is a code smell in simple terms?

    +

    A sign in the code that its design may be wrong — like a very long function or copied logic. The code still works, but it is harder to change safely.

  • Are code smells bugs?

    +

    No. A bug produces a wrong result; a code smell is working code that is hard to maintain. Smells make future bugs more likely, which is why teams fix them over time.

  • What are the types of code smells?

    +

    Five categories: bloaters (code too large), object-orientation abusers, change preventers, dispensables (code that isn't needed) and couplers (classes too tightly linked).

  • What are the most common code smells?

    +

    Long Method, Large Class, Duplicate Code, Long Parameter List, Feature Envy, Primitive Obsession and Dead Code.

  • What is a code smell in SonarQube?

    +

    SonarQube's term for a maintainability issue — code that works but is confusing or hard to change, such as high cognitive complexity, duplication or unused code.

  • How do you fix a code smell?

    +

    With a small, targeted refactoring — extract a function, move a method to the data it uses, introduce a type — ideally with tests in place and in a separate pull request.

  • Can tools detect code smells automatically?

    +

    Partly. Linters and static analysis detect measurable smells such as length, complexity and duplication. Design smells need a person or an AI reviewer that understands the codebase.