Clean code — Does code smell? Part 1

Leonardo Herdy Marinho
8 min readFeb 2, 2024

--

Yes, and sometimes it stinks! Some time ago I started to read the book “Clean Code — Robert C. Martin”. The famous Robert, or, more popularly known as “Uncle Bob” explained in his book about some things, in special code smells. Looks fun to think about code and smells, it looks strange. Code is virtual and smells are from the physical world. Code can smell and in the biggest part of the time it stinks. Let’s understand better about it!

Code smells are indicators or characteristics in the source code that suggest there might be a problem or a violation of good design principles. These aren’t strict rules but rather warning signs that the code may need refactoring. Following Uncle Bob’s Clean Code principles, here are some common code smells:

Long Methods/Functions:

  • Smell: Methods or functions that are excessively long.
  • Solution: Break down long methods into smaller, more focused ones.

Large Classes:

  • Smell: Classes that are too large and have too many responsibilities.
  • Solution: Split classes into smaller, more cohesive ones, each with a single responsibility.

Duplicate Code:

  • Smell: Repeated code fragments in different parts of the system.
  • Solution: Extract common code into methods or functions to eliminate duplication.

Comments:

  • Smell: Excessive or misleading comments that indicate code is not self-explanatory.
  • Solution: Refactor code to make it more readable and remove unnecessary comments.

Nested Code Blocks:

  • Smell: Deeply nested if statements or loops.
  • Solution: Simplify logic by breaking down complex conditions or using early returns.

Inconsistent Naming:

  • Smell: Inconsistent naming conventions for variables, methods, or classes.
  • Solution: Maintain a consistent naming convention throughout the codebase.

Feature Envy:

  • Smell: A class/method accessing the data of another class more than its own data.
  • Solution: Move the functionality to the class that owns the data.

Switch Statements:

  • Smell: Use of switch statements, especially when they appear in multiple places.
  • Solution: Replace switch statements with polymorphism or use strategy patterns.

Data Clumps:

  • Smell: Multiple parameters passed together in many methods.
  • Solution: Encapsulate the related data into a separate class or structure.

Primitive Obsession:

  • Smell: Using primitive data types (int, string) instead of creating meaningful abstractions.
  • Solution: Introduce new classes or enums to represent concepts more explicitly.

Code Comments:

  • Smell: Comments that explain what the code does instead of making the code itself more understandable.
  • Solution: Improve the code to be self-explanatory and remove unnecessary comments.

Tight Coupling:

  • Smell: Classes or modules that are highly dependent on each other.
  • Solution: Decouple classes by introducing interfaces, dependency injection, or other design patterns.

Long Methods/Functions

This code smell refers to the situation where a method or function is excessively long, meaning it contains a large number of lines of code. Long methods can make code harder to understand, maintain, and debug. They tend to violate the Single Responsibility Principle (SRP), as they often perform multiple tasks within the same method.

Here’s why long methods are considered a code smell:

Readability:

  • Long methods can be challenging to read and understand. Developers may find it difficult to grasp the entire logic at once, leading to confusion.

Maintenance:

  • Modifying or fixing bugs in long methods is more error-prone because the complexity is higher. Changes in one part of the method might unintentionally affect other parts.

Testing:

  • Writing comprehensive tests for long methods is more difficult. It’s challenging to cover all possible scenarios and edge cases in a single test.

Reusability:

  • Long methods are less likely to be reusable in other parts of the code. Smaller, focused methods are more modular and can be reused more easily.

To address the issue of long methods, you can follow these strategies:

Decompose into Smaller Methods:

  • Identify distinct tasks or responsibilities within the long method and extract them into separate, well-named methods. Each method should have a clear purpose.

Single Responsibility Principle (SRP):

  • Ensure that each method adheres to the Single Responsibility Principle. A method should do one thing and do it well.

Use Descriptive Names:

  • Choose meaningful and descriptive names for methods. This helps in understanding the purpose of each method without delving into its implementation.

Limit the Number of Lines:

  • Aim to keep methods reasonably short. While there’s no strict rule on the maximum number of lines, a method with more than 20–30 lines might be a candidate for refactoring.

Code Reviews:

  • Encourage code reviews within the development team to identify and address long methods early in the development process.

Here’s an example of refactoring a long method:

By breaking down the long method into smaller, focused functions, the code becomes more readable, and maintainable, and follows the principles of clean code. It’s simple to create unitary tests, to find bugs, and and solve issues faster than comparing with a “single method application”. This is also a principle that leads to functional programming.

Large Classes

This code smell refers to classes that have grown too large and take on too many responsibilities. Large classes violate the Single Responsibility Principle (SRP) and can make the codebase harder to understand, maintain, and extend. Just like with long methods, large classes can be indicative of design issues that need to be addressed.

Here are some reasons why large classes are considered a code smell:

Complexity:

  • Large classes tend to have higher complexity, making it challenging to understand their behavior and relationships with other classes.

Maintainability:

  • Modifying large classes becomes more error-prone and time-consuming. Changes in one part of the class may unintentionally affect other parts.

Testing:

  • Testing large classes comprehensively can be difficult. It’s challenging to cover all possible scenarios and interactions within a single test.

Readability:

  • Code readability suffers when there’s a large amount of logic within a single class. It becomes harder for developers to quickly understand the purpose and responsibilities of the class.

Reusability:

  • Large classes are less likely to be reusable in other parts of the code. Smaller, focused classes are more modular and can be reused more easily.

To address the issue of large classes, consider the following strategies:

Single Responsibility Principle (SRP):

  • Ensure that each class has a single responsibility. If a class is doing too many things, break it down into smaller classes, each with a specific responsibility.

Encapsulation:

  • Encapsulate related functionality into smaller, cohesive classes. This helps in creating a more modular and maintainable codebase.

Composition over Inheritance:

  • Favor composition over inheritance. Instead of adding more functionality to an existing class through inheritance, consider using composition to assemble smaller, more focused classes.

Refactoring:

  • Regularly review and refactor your code. If you notice a class becoming too large, consider refactoring it into smaller, more manageable pieces.

Here’s a simple example of a large class:

Refactoring, it should look like like this:

In this refactoring example, the responsibilities of the original OrderProcessing class have been broken down into three smaller classes (OrderProcessor, OrderValidator, and InvoiceGenerator). Each class now has a single responsibility, making the code more modular and adhering to the principles of clean code.

Duplicate Code

The “Duplicate Code” code smell refers to identical or very similar code fragments in different parts of a codebase. Duplicate code can lead to various problems and violates the DRY (Don’t Repeat Yourself) principle. Identifying and eliminating duplicate code is crucial for maintaining a clean, maintainable, and efficient codebase.

Here are some reasons why duplicate code is considered a code smell:

Maintenance Challenges:

  • If a bug is discovered or a change is required in duplicated code, you must make the same modification in multiple places. This can be error-prone and time-consuming.

Readability and Understanding:

  • Duplicate code makes the codebase more difficult to read and understand. Developers may need to analyze multiple locations to comprehend the logic or behavior of the code.

Consistency:

  • Ensuring consistency across duplicated code fragments can be challenging. It’s easy for inconsistencies to arise over time, leading to subtle bugs.

Testing Complexity:

  • Testing becomes more complex when the same logic is duplicated in multiple places. Each occurrence must be tested separately, increasing the testing effort.

Code Size and Maintainability:

  • Duplicate code contributes to unnecessary code size, making the codebase larger than necessary. This can hinder maintainability and increase the learning curve for new developers.

To address the issue of duplicate code, consider the following strategies:

Extract Methods/Functions:

  • Identify duplicated code fragments and extract them into separate methods or functions. Call these methods or functions from the places where the code was duplicated.

Create Utilities or Helper Functions:

  • If the duplicated code serves a common utility function, consider creating a utility class or set of helper functions that can be reused across the codebase.

Use Inheritance or Composition:

  • If the duplicated code is related to class behavior, consider using inheritance or composition to share common functionality among classes.

Refactoring:

  • Regularly review and refactor your code to eliminate duplication. Tools like linting tools or IDE features can help identify duplicated code.

Here’s a simple example of refactoring to eliminate duplicate code:

The refactored code should be something similar to:

For the previous code, we aren’t considering yet other code smells, like magic numbers, too many parameters, and similar. Our focus here is to remove duplicated code and organize following the Don’t Repeat Yourself principle. In this refactoring example, the duplicated code for calculating the area and perimeter of a rectangle has been extracted into a common function (calculateRectangleProperty). This eliminates the need for duplicating the logic in both the calculateAreaOfRectangle and calculatePerimeterOfRectangle functions. The common function takes an additional parameter to specify the operation to be performed.

In this article, we explored some common code smells and their solutions. We discussed the importance of identifying and addressing long methods, large classes, and duplicate code. By following principles such as the Single Responsibility Principle and Don’t Repeat Yourself, we can improve the readability, maintainability, and reusability of our code. However, these are just a few examples of code smells, and there are many more to consider. In the next article, we will continue our exploration of clean code principles and dive deeper into other code smells and how to refactor them. Stay tuned!

Continue reading: Clean code — Does code smell? Part 2

References

  • Martin, R. C. (2009). Clean Code: A Handbook of Agile Software Craftsmanship. Prentice Hall.
  • Rodrigo Branas Clean Code YouTube Channel

--

--

Leonardo Herdy Marinho
Leonardo Herdy Marinho

Written by Leonardo Herdy Marinho

I am a Master in computer science, senior software engineer (mobile focused), author, and curious.

No responses yet