For the past couple of years as an SE, I thought the brain, StackOverflow and perhaps, just maybe my fingers are the only tools needed to excel at this job, however, at some point along the line, I realised that the nose 👃 could be instrumental too. Before you start doubting your abilities as an Engineer, just know, that this is all in a figurative and derivative sense. A smell is something you can quickly pick without deep thought or process, this same allegory is applicable to this thing that pays us. Do you remember when you looked at a method and something in your heart just threw off signals (off as in off like off ok!)? If yes, then your programming nose is as functional as the last code you wrote, if not then I really don’t know how I can help you (just kidding read on, this article is for you).
Code just like any entity in life often snitches on itself, as the good detective you are, it’s time you see the signs when they avail themselves. By definition, a code smell is any characteristic in the source code of a program that possibly indicates a deeper problem. The concept behind this is beyond the mere amalgamation of functional failures but rests in the premise that it doesn’t have to fail to look weird.
In the spirit of Kaizen (the Japanese in me is speaking now), it is imperative to always continually improve, as such code refactoring will always be with us. In this context, I believe after reading this article you will have an idea of what to improve in your codebase.
Let’s look at the different types of stinks, lol btw it doesn’t have a nose but it can stink. We have 5 major groups as described in this book (Refactoring by Martin Fowler and Kent Beck) –, namely Bloaters, OOP Abusers, Change Preventors, Dispensables and Couplers.
At a high level, it seems like anything that violates SOLID principles can potentially be thought of as a code smell, but as you will see it extends beyond the SOLID presupposition.
These are the most obvious, not because they look any different but because almost everyone I know has had a bloated stomach before, “Bien sûr” –
pardon my french, it’s definitely not a pleasant experience. Unless it’s an extremely rare situation it is not customary to have a method with more than 1000 lines of code or a signature of the same with more than 7 parameters. A bookish definition of bloaters will be as follows – Bloaters are code, methods and classes that have increased to such mad proportions which render them hard to maintain.
Examples are long methods, long parameter lists, primitive obsessions and data clumps.
Long parameter lists are often a consequence of primitive obsession, clever programmers often obsess over the use of the heap, and as such, they will prefer to use primitive types as much as possible. It’s a noble quest but anything done in excess can be an issue.
fun iStink(name: String, address: String, phoneNumber: Int, breakUpDate: String, duration: String)
fun iStinkLess(stinkLessArguments: StinkLessArguments)
Where the arguments are just a data class with the properties above. Don’t worry a lot about overloading the heap, garbage collectors often don’t miss, unless maybe there is a leak somewhere.
Where do I start on this, I know we all love OOD (Object Oriented Design) but sometimes we overuse it, remember that time you created an interface that’s only implemented by one class, yes you do remember? Why did you do that? If maybe it was for a test then I might understand but oh well we do that a lot. Object Oriented Abuse stems from the incorrect usage of object-oriented design.
Examples are –
- Alternative classes with a different interface – When two or more distinct classes seem to hold similar responsibilities but have different signatures. A culprit in most cases is util classes.
- Switch Statements – Complex switch, when or if else clauses
- Refused bequest – This one here abuses inheritance like no man’s business, imagine a subclass that overrides all the superclass methods.
- Temporary fields – Often happen when you create a field that is only used by one method in a class, as such this field will lie idle for most of the time during the Class’s existence
These are probably the most annoying of them all, imagine one change cascading to a million other places. i.e using magic numbers instead of constants, magic number changes and you have to update wherever the same exists.
- Divergent Change – Many changes made to a single class (kinda violates Single Responsibility Principle)
- Shotgun Surgery – Single change that cuts across multiple classes at the same time.
- Parallel Inheritance Hierarchies – When an inheritance tree depends on another inheritance tree.
If it is not needed, don’t include it. So these don’t really have an effect on code readability or functionality but however, but their presence may just be unpleasant. Examples are:
- Comments – Yes comments can be smelly sometimes, the best comment is the name of your class or method.
- Data Classes – We don’t always need them, sometimes it’s fair enough to use Pairs or Triple. Unless you really need one don’t create it.
- Duplicate code
- Dead code
- Lazy class – If a class doesn’t capture your attention anymore it’s probably not needed anymore.
- Speculative Generality – Yes that unused class or method. Often a result of “just in case” code.
Often a result of excessive coupling.
- Feature Envy – Occurs when a class method often accesses the data of another class more than it does its members.
- Inappropriate Intimacy – I like to call this class promiscuity when one class uses methods and data internal to another class.
- Message Chaining -> Imagine a series of classes that cyclically depend on each other to perform a certain function, if one fails the entire chain fails.
- Middle Man -> A class that exists to delegate work to another class should not exist.