Table of Contents
In Apiumhub we always focus on quality and best practices in Software development. When we don’t start working on a project from scratch, we very often find code smells and this article is about it. Martin Fowler very well explained one day what is a code smell, it is a surface indication that usually corresponds to a deeper problem in the software system. And the term was first coined by Kent Beck while helping Martin with the Refactoring book, which I highly recommend to read. Well, if you are interested in this topic, here you may find a list of other very useful software development and software architecture books.
One of the nice things about code smells is that it’s easy for inexperienced people to spot them, even if they don’t know enough to evaluate if there’s a real problem or to correct them. Many companies organize “code smells of the week” and ask developers to look for the smell and bring it up with the senior members of the team. Doing it one smell at a time is a good way of gradually teaching people on the team to be better programmers.
Common Code Smells
Developers are typically trained to look out for logical errors that have been accidentally introduced to their code. Such errors will range from forgotten edge cases that have not been handled to logical bugs that cause entire systems to crash. But what about the other issues that don’t affect the way the system works? For example, the design issues that make the system hard to maintain, and increase the chance of bugs in the future, etc.? Code Smells are signals that your code should be refactored in order to improve extendability, readability, and supportability.
Here you have the most common code smells:
Bloaters are code, methods and classes that have increased to such proportions that they are hard to work with. Usually these smells do not crop up right away, rather they accumulate over time as the program evolves. For example: Long Method, Large Class, Primitive Obsession, Long Parameter List, Data Clumps.
All these smells are incomplete or incorrect application of object-oriented programming principles. For example, Switch Statements, Temporary Field, Refused Bequest, Alternative Classes with Different Interfaces
These smells mean that if you need to change something in one place in your code, you have to make many changes in other places too. Program development becomes much more complicated and expensive as a result. For example: Divergent Change, Shotgun Surgery, Parallel Inheritance Hierarchies
A dispensable is something pointless and unneeded whose absence would make the code cleaner, more efficient and easier to understand. For example: Comments, Duplicate Code, Lazy Class, Data Class, Dead Code, Speculative Generality.
All the smells in this group contribute to excessive coupling between classes or show what happens if coupling is replaced by excessive delegation. For example, Feature Envy, Inappropriate Intimacy, Message Chains, Middle Man, Incomplete Library Class.
Let’s look at some of them in details, the ones that are found the most:
The majority of a programmer’s time is spent reading code rather than writing code. Apart from the difficulty of having to keep a lot of complex logic in mind whilst reading through a long method, it is usually a sign that the method has too many responsibilities. Long methods make code hard to maintain and debug. If it is not possible to view the whole method on your smartphone screen, consider breaking it up into several smaller methods, each doing one precise thing.
When developer fixes a bug, but same symptoms are faced again later on, this can be the result of code duplication, and a bug being fixed in one occurrence of the imperfect code but not in the duplicated versions. This poses an overhead in terms of maintenance. When developers are not aware of the duplication, they only know to fix the occurrence they have come across. Take care of the repeated code blocks and extract them out into a single place – don’t repeat yourself!
If a class inherits from a base class but doesn’t use any of the inherited fields or methods, developers should ask themselves if inheritance really is the right model. Signs of this code smell may be that the inherited methods go unused, or are overridden with empty method parts.
Inheritance should be used when a class wants to reuse the code in its superclass. If the classes diverge and the subclass no longer needs that functionality, the hierarchy should be broken and delegation considered instead. And to keep some inheritance, remove the unused fields and methods from the subclass and create a new layer that the objects can inherit from.
Where multiple method calls take the same set of parameters, it may be a sign that those parameters are related. To keep the group of parameters together, it can be useful to combine them together in a class. This can help aid organisation of code.
When a class exists just to delegate to another, a developer should ask themselves what its real purpose is. Sometimes this is the result of a refactoring task, where logic has been moved out of a class gradually, leaving an almost empty shell.
Primitive types give little in terms of domain context. Wrap them in a small class to represent the idea. Often this kind of class is expanded to include methods to add to the class.
It is when a class is commonly changed in different ways for different reasons and suffers many kinds of changes. So, ideally, you should have a one-to-one link between common changes and classes.
It is basically when you want to make a kind of change, you need to make a lot of little changes to a lot of different classes. The problem is that when the changes are all over the place, they are hard to find, and it’s easy to miss an important change.
It is when a method does not leverage data or methods from the class it belongs to. Instead, it requires lots of data or methods from a different class.
When you use multiple primitive data types to represent a concept such as using three integers to represent a date. Don’t be afraid to use small objects for small tasks such as money classes that combine number and currency.
A class that isn’t doing enough to pay for itself, but remember that each class you create costs money to maintain and understand.
Type Embedded in Name
Avoid placing types in method names; it’s not only redundant, but it forces you to change the name if the type changes.
Does the name of the method succinctly describe what that method does? Could you read the method’s name to another developer and have them explain to you what it does? If not, rename it or rewrite it. Pick a set of standard terminology and stick to it throughout your methods. For example, if you have “Open”, you should probably have “Close”.
Delete code that isn’t being used. Make it clean and simple.
And many others, if you want we can discuss them in the comments section below!
And if you are interested in best practices in software development, I highly recommend you to subscribe to our monthly newsletter to receive latest software development books, tips, and upcoming events.