Image represents a media kit with boilerplate, logos and more

Definition and Guide

code smells: developer's guide

Code smells are warning signs in your code that hint at deeper issues. These aren't errors and the code will still work, but they can make future development harder and increase the risk of bugs.

Table of Contents

Start your free trial

Verify all code. Find and fix issues faster with SonarQube.

Get started

TRUSTED BY 7M+ DEVELOPERS & 400K+ ORGANIZATIONS

What is a code smell?

Code smells are defined as categories of issues in code that may lead to deeper problems over time. They are similar to warning indicators that arise due to potential bad coding practices. 

While code smells are not errors or bugs per se and do not affect the functionality of the software, they indicate weaknesses in design that can slow down development or increase the risk of bugs or failures down the line.

Some common examples of code smells include issues such as large classes and long methods – i.e. one function, trying to do too much, or methods that are long to understand. 

Another example is duplicated code – where the same piece of code is repeated in multiple places. 

Issues like these can lead to clunky code, increase the cognitive complexity of the codebase, and contribute toward maintainability issues and increased technical debt in software. 

What are examples of code smells?

Code smells are patterns in source code that, while not necessarily bugs, indicate deeper design or maintainability issues. They make code harder to understand, extend, and review, and they often lead to defects over time if left unaddressed. Common code smells include:

  • High cognitive complexity – Code that requires too much effort to understand because of deep nesting, complex conditions, or intertwined logic.
  • Duplicated logic – Repeating the same code across functions or modules, increasing the risk of inconsistent updates.
  • Long methods or large classes – Functions or classes doing too much, making responsibilities unclear.
  • Feature envy – A method accesses data from another class excessively rather than working with its own.
  • Magic numbers or strings – Hard-coded values that make code less readable and less flexible.
  • Dead code – Unused variables, unreachable branches, or obsolete functions.
  • Excessive parameters – Functions with too many arguments, reducing readability and increasing misuse risk.
  • Inconsistent naming – Names that don’t communicate intent clearly.
  • Mutable shared state – Non-obvious side effects, making code harder to reason about.
def process_user(user):

    if user.is_active():             # +1 (if)

        if user.has_profile():       # +1 (if) +1 (nested)

            ... # process active user with profile

        else:                        # +1 (else)

            ... # process active user without profile

    else:                            # +1 (else)

        if user.has_profile():       # +1 (if) +1 (nested)

            ... # process inactive user with profile

        else:                        # +1 (else)

            ... # process inactive user without profile

Why are code smells important?

Code smells are crucial indicators to the state of software because they are early warning signs of possible issues that could arise in the codebase. They are important because they can identify places where the code may be vulnerable to problems in the future even though it is working properly now. 

Developers can prevent fundamental design flaws, maintainability issues, and scalability issues before they become more serious issues by catching code smells. 

Detection of code smells may seem intrusive and noisy at development time, but they encourage developers to follow recommended coding standards and best practices, which helps to create a high-quality code culture inside development teams. 

They serve as guiding concepts for developers working on refactoring projects, pointing them in the direction of cleaner, more modular, and maintainable code. 

Along with this, code smells are instructional tools that aid developers in developing a deeper comprehension of software design patterns and ideas.

Developers can refine their abilities, develop an awareness of and appreciation for code quality, and add to the continuous enhancement of the codebase by identifying and fixing code smells. 

By giving developers a shared language to discuss and manage code quality issues, code smells promote cooperation and communication.

By consistently detecting and fixing code smells, software projects can reduce technical debt, increase long-term maintainability, and raise overall software quality.

Common categories of code smells

Code smells typically fall into several common categories that help developers quickly identify the type of design issue present and apply the right refactoring strategy.

  • Bloaters
    These arise when code becomes overly large or overloaded, such as with long methods, large classes, or lengthy parameter lists. They often signal poor distribution of responsibilities and make code harder to read and maintain.
  • Object-Oriented Abuse
    These smells come from misusing object-oriented principles, such as refused bequest or temporary fields. They usually indicate flawed inheritance structures or poorly defined abstractions.
  • Dispensables
    These are unnecessary pieces of code—like dead code, unused variables, or redundant comments—that add no real value. Removing them simplifies the codebase and improves readability.
  • Couplers
    These smells highlight excessive dependencies between classes, such as inappropriate intimacy or feature envy. High coupling reduces modularity and makes the code more fragile and difficult to evolve.

Recognizing these categories gives developers a clearer framework for identifying code smells and understanding the underlying design principles they may be violating. This awareness supports more effective refactoring and contributes to a healthier, more maintainable codebase.

What are ways to address code smells?

Addressing code smells requires a deliberate plan that begins by identifying the underlying cause of each smell and prioritizing which ones to fix first. Not all smells are equal—some significantly impact readability, maintainability, and risk of bugs, while others have minimal long-term effect. Developers should evaluate smells based on severity, frequency of use, and the potential return on investment. This prioritization helps teams focus on issues that most meaningfully improve the health and sustainability of the codebase.

Once the root cause is understood, the next step is applying the right refactoring techniques. Many smells, such as duplicated logic, large classes, or overly complex functions, can be addressed through methods like Extract Method, Extract Class, simplifying conditionals, or introducing clearer names and data structures. These strategies reduce friction in the code, improve clarity, and minimize the likelihood of errors. Tools like SonarQube help identify patterns such as cognitive complexity, duplication, and poor structure so developers know exactly where to focus their efforts.

In the earlier example, the Python snippet suffered from unnecessary nesting and excessive lines inside a single block of logic—classic contributors to high cognitive complexity. When code becomes difficult to follow, developers spend more time deciphering it than enhancing it, slowing down future changes and increasing maintenance costs. By refactoring the code into smaller, focused functions, complexity is distributed across simpler units, and the cognitive load drops significantly. The result is code that is easier to read, test, modify, and build upon.

def process_user(user):

    if user.is_active():             # +1 (if)

        process_active_user(user)

    else:                            # +1 (else)

        process_inactive_user(user)

def process_active_user(user):

    if user.has_profile():           # +1 (if) +1 (nested)

        ... # process active user with profile

    else:                            # +1 (else)

        ... # process active user without profile

def process_inactive_user(user):

    if user.has_profile():           # +1 (if) +1 (nested)

        ... # process inactive user with profile

    else:                            # +1 (else)

        ... # process inactive user without profile

Using a built-in name for a variable shadows the built-in within the local scope, preventing access to the original built-in inside that function or file. Although this may not be encountered by a senior web developer, this is not a good coding practice as the builtin won’t be accessible through its original name if used this way. 

 def a_function():

    int = 42

The proper way to fix this is to use a proper variable:

def a_function():

    value = 42

Another example from above is files having too many lines of code.

Large files often indicate the presence of true code smells such as Large Class, Long Method, or Feature Envy.

To make code more maintainable, files above a specific threshold should be refactored into smaller files whose code focuses on well-defined tasks. 

Those smaller files will be easier to understand and test.

As soon as code smells have been located, developers can use a variety of refactoring strategies to get rid of them without changing the code's outward behavior. 

Developers can divide lengthy, complicated procedures that result in code smells into smaller, easier-to-manage methods that each handle a specific task.

Improving code health through refactoring

Refactoring—the process of restructuring code without altering its external behavior—is a primary technique for addressing code smells and improving long-term code health. Its purpose is to enhance readability, modularity, and overall quality so developers can work more efficiently and confidently as the codebase evolves. Because refactoring changes the internal structure of code, it requires careful thought and context, not just mechanical updates.

Successful refactoring involves deliberate trade-off analysis, since rushed or poorly planned changes can introduce new issues or disrupt existing functionality. Eliminating code smells therefore requires a balanced approach that blends technical expertise with an understanding of project goals, constraints, and timelines. Teams that consistently identify and address code smells early foster a culture of continuous improvement, ensuring that their codebase remains maintainable, reliable, and secure as it grows.

To ensure safe and effective refactoring, developers should pair structural changes with thorough testing to avoid regressions. Documenting the rationale behind each refactoring step helps preserve codebase clarity and supports future maintenance efforts. Practices such as code reviews and pair programming further strengthen this process by enabling knowledge sharing, validating design decisions, and ensuring alignment across the team.

The cost of ignoring code smells

Ignoring code smells may seem harmless in the short term, especially when the software still functions correctly, but the long-term consequences can be significant. Unaddressed smells accumulate into technical debt, making the codebase harder to understand, modify, and debug as it grows. Over time, this increases the effort required for even simple changes, creating a compounding cost that slows down development and reduces overall productivity.

As technical debt builds, onboarding new developers becomes more difficult because they must first navigate unclear structures, inconsistent patterns, or overly complex logic before contributing effectively. This not only raises training costs but also introduces greater risk of mistakes as newcomers struggle to interpret convoluted code. Teams may also experience a rise in defects, as fragile or tightly coupled code is more prone to break when updated, ultimately leading to slower release cycles and decreased delivery velocity.

Sonar and code smells

SonarQube Server and SonarQube Cloud can help developers identify and prevent code smells along with a variety of other issues (bugs and security vulnerabilities, etc.). 

Both natively integrate into your CI/CD pipelines to find and fix issues early in the software development phase before they create difficulties in production. 

SonarQube for IDE, a companion tool to SonarQube Server and SonarQube Cloud, is an IDE plugin that provides real-time feedback within the developer's editor, highlighting code issues (including code smells) as developers write quality and secure code. 

  • Follow SonarSource on Twitter
  • Follow SonarSource on Linkedin
language switcher
English

© 2025 SonarSource Sàrl. All rights reserved.