Code is the DNA that makes your programs and IT services function. It’s the foundation of any development project. And something as small as an out-of-place character can mean the difference between a development project that works as it should and one that doesn’t perform well. Needless to say, it’s important for developers to be able to identify potential weaknesses in code. Code smell is an indicator that enables developers to answer a mission-critical question:
“Is there a better way?”
What’s code smell?
In short, code smell is a warning sign that can only be observed by someone who has a fine-tuned “code nose.” This typically means it is observable by developers and is a surface indicator of a deeper problem.
The types of problems that can be indicated by a code smell are not usually bugs that will cause an entire system crash – and developers are well trained to uncover logic errors that cause bugs and system failure. These are much more subtle than logic errors and indicate problems that are more likely to impact overall performance quality than cause a crash.
Classification of code smells and solutions
There are five unique classifications of bad code smell that you might encounter as a developer. Here’s what to look for:
Smells in the bloaters category include long-method, large class, primitive obsession, long parameter list and DataClumps. Let’s take a moment to examine each of these in more detail:
- Long method. As a general rule of thumb, methods should be questioned when they are longer than ten lines. If a method contains too many lines of code, it can impact performance. This occurs when developers add to code, but they don’t take anything out. If you need to comment within a method, you should split out the lines of code into a new method. If an explanation is required on just a single line of code, it should still be separated from the original method. Using descriptive headings can help tell other developers what this method does without having to look deeply into it.
- Large class. Classes start small and bloat over time. A class is considered large when it contains many methods, fields, and/or lines of code. Here’s a simple fix – if a class has too many functions, split it up using commands like Extract Class, Extract Subclass, and Extract Interface.
- Primitive obsession. This code smell typically occurs when a developer has decided to make a primitive field to store data instead of creating a whole new class. It includes issues like using primitives in place of small objects and the strategic use of constants. There are several different ways to address this problem. Firstly, if you have a lot of primitive fields, see if you can group them by class. Use commands like Introduce Parameter Object and Preserve Whole Object if the primitive values are used in the parameters of the method. If the primitives contain arrays, change them with the Replace Array with Object command. This may seem like a lot of effort but the benefit is the code becomes more organized, understandable, and flexible. It sets it up for more building without bloating.
- Long parameter list. If you include too many parameters for a method it will lead to a code smell. Three to four parameters maximum is the standard. Anything beyond that can cause a problem. To begin resolving this, replace arguments with Method Calls where possible. Or use Preserve Whole Object to pass an entire object to a method instead of groupings of data. This will simplify the code making it shorter and easier to read.
- DataClumps. Different pieces of code that contain the same variable groups should be turned into their own classes. When this fails to occur you get a DataClumps code smell. Like the above solutions, resolving this means simplifying the data used. Introduce Parameter Object and Preserve Whole Object commands are satisfactory ways of doing just that.
The object-orientation abusers class contains its own challenges. These include switch statements, temporary field, refused bequest, and alternative classes with different interfaces. The common ground between all of these is that they all relate to errors in object-orientation programming.
- Switch statements. Switch and Case statements are relatively normal in object-oriented code. A single switch can have code placed throughout a program. It’s essential to find all places where that code exists and modify them when making a change. There are several ways to address this. One such way is to use a command like Extract Method and Move Method to isolate a switch code and put it in the correct class. The command you use is going to depend on the conditions of the switch. You may need to Introduce Null Object or Replace Conditional with Polymorphism. The key to fixing it is to understand what you’re trying to accomplish and whether you need to move the method, create a new class, or break into smaller methods to enhance understanding and performance.
- Temporary field. Code smell occurs with temporary fields when they’re empty. That’s because temporary fields get filled under specific conditions. Most often a problem occurs when temporary fields are created for use in an algorithm and go unused the majority of the time. These temporary fields can be moved into a separate class all of their own via the Extract Class command, or integrated into the position of conditional code via Introduce Null Object.
- Refused bequest. This happens when a subclass doesn’t use all of the methods and properties passed down by its parent class. The unused methods can cause a code smell if not properly addressed. There are two ways to successfully address this, but first you have to decide if the subclass really belongs to its parent. If the answer is no, break it out with the Replace Inheritance with Delegation Command. If yes, remove any superfluous fields and methods from the subclass to clean it up.
- Alternative classes with different interfaces. This presents when two classes perform identically to one another. It’s usually caused by a simple mistake that occurs when a developer doesn’t realize another class already exists that performs the same way. You can solve this by renaming problematic methods to make them the same. Or you can make parts of them the same like the parameters and implementation of methods. If the duplication only exists in part of the method, use Extract Superclass to create a subclass of methods. Once the appropriate changes have been made you can delete the duplicate class.
The smell known as change preventers hinders further development as the name suggests. Basically, if changes are made in one class or method then changes have to be made in other classes or methods as well.
- Divergent change. This smell means you have to make changes to many methods within a class because you made changes to a class. An example of this would be adding a new product to an e-commerce platform and the new product impacts a number of functions on the site. This usually happens when people who don’t know code well enough try to copy and paste or use code from other sites. Without the knowledge of personalization, errors occur. To correct this, you can use the Extract Class command to separate the behavior from the class. If the issue stems from multiple classes having the same behavior, you may want to combine those instead.
- Shotgun surgery. This occurs with any modification that sets off a cause-and-effect chain, where you have to make minute changes to a number of classes. This usually happens after behaviors have been split up into many classes. To put them back together again use Move Method and Move Field to create a single class of relevant behaviors.
- Parallel inheritance hierarchies. These happen when you find that while creating a subclass for one, you have to do the same for other classes. Making changes becomes harder as new classes are created. Luckily, this problem is solvable in two steps. Point instances of one hierarchy to the other, then de-dupe the referring class using Move Method and Move Field. That said, it may be prudent to ignore this smell at times. Sometimes trying to untangle a mess of parallel inheritance hierarchies creates an even bigger one.
Dispensables live up to their name. It’s coding that wouldn’t impact the program if it was removed completely. In most cases, removing dispensables results in cleaner and more understandable code. Each type of dispensable code smell is explained below, but since these are dispensables they have a quick and easy fix – just delete them.
- Lazy class. A lazy class code smell indicates that code is underperforming, or essentially not doing enough to afford your attention.
- Data class. It’s a class that’s just a data set for other parts of the program and is not logical and thus unnecessary.
- Duplicate code. As the name suggests, duplicate code occurs when almost identical code is implemented more than once.
- Dead code. This means that a code does not function or serve any purpose.
- Speculative generality. This is a symptom of trying to anticipate all the coding needs of a specific program from the start. As you develop the project, unnecessary code can be removed.
- Comments. This is triggered when a method is filled with comments. Many are likely unnecessary to the overall functioning of the program.
This grouping contains smells that encourage coupling between classes.
- Feature envy. This smell happens erroneously when features have been moved from one method to another. The result is the method is confused and tries to access another object’s data. Implementing best practices is the best way to avoid feature envy. When changing multiple objects at once, keep everything in the same place. If you absolutely have to move a method use the Move Method command. If you’re only moving part of the method use Extract Method to achieve the result you desire.
- Inappropriate intimacy. This smell is an indicator that one class is intermingling with another class via fields and methods that belong to the other. A best practice is to check classes that have spent a lot of time with one another for this issue. If the referred class doesn’t need its parts, resolve this with the Move Methods and Move Fields command. If it’s more complicated than this, you can Extract Class but you’ll also have to use the Hide Delegate command on the class so it works for both parties. For mutually interdependent classes, use the command Change Bidirectional Association to Unidirectional. If the existing relationship is between a superclass and subclass, use a command to replace the inheritance of the subclass.
- Message chains. A message chain is a series of calls in code that start with a single client. The client requests an object, and that object requests another. This leads to a chain of objects stemming from the client. If a change is made in any of these objects you need to make changes in the client also. By using the Extract and Move Method commands, move the object at the end of the command to the beginning. Otherwise, you can delete it if it’s excessive.
- Middle man. Cut out the middleman by remedying smells whose only purpose is to delegate action from one class to another. This is sometimes an offshoot effect of fixing message chains as described above. The command Remove Middle Man is a fix for this issue.
Is There a Better Way Than Code Smell?
While code smells don’t usually deal with critical programming errors, they do impact the overall user experience (UX). In this technology-driven world, UX has become an important topic of conversation working its way from being an added bonus to an essential component of any program or website. Even Google has updated its algorithm in recent years to encourage greater UX.
Anticipating the needs and desires of end-users is an important part of development. And one way to save time in the development process is to acquire a fine-tuned code nose that can help sniff out potential problems.
If this is new information to you, I would encourage you to spend some time with code smells to perfect your skills. Maybe, for now, it comes down to just asking “Is there a better way?” as you work through your code.