Your code smells because there are likely many instances where it could be edited or improved.
Most of these smells are just hints of something that might be wrong. Therefore, they are not required to be fixed per se… (You should look into it, though.)
Previous Code Smells
You can find all the previous code smells (Part I - XLIII) here.
Let's continue...
Code Smell 216 - Fat Interface
You should not define too much behavior together
TL;DR: Split your interfaces
Problems
- Interface Segregation Principle Violation
- Coupling
Solutions
- Split the interface
- Use composition instead of inheritance
Context
The term "Fat Interface" emphasizes that the interface is overloaded with methods, including those that may not be necessary or used by all clients.
The interface violates the principle of segregating interfaces into smaller, more focused contracts.
Sample Code
Wrong
interface Animal {
void eat();
void sleep();
void makeSound();
// This protocol should be common to all animals
}
class Dog implements Animal {
public void eat() { }
public void sleep() { }
public void makeSound() { }
}
class Fish implements Animal
public void eat() { }
public void sleep() {
throw new UnsupportedOperationException("I do not sleep");
}
public void makeSound() {
throw new UnsupportedOperationException("I cannot make sounds");
}
}
class Bullfrog implements Animal
public void eat() { }
public void sleep() {
throw new UnsupportedOperationException("I do not sleep");
}
public void makeSound() { }
}
Right
interface Animal {
void move();
void reproduce();
}
// You can even break these two responsibilities
class Dog implements Animal {
public void move() { }
public void reproduce() { }
}
class Fish implements Animal {
public void move() { }
public void reproduce() { }
}
class Bullfrog implements Animal {
public void move() { }
public void reproduce() { }
}
Detection
- [x]Manual
We can check the size of the interface protocol
Tags
- Cohesion
Conclusion
Favoring small, reusable code components promotes code and behavior reuse.
Relations
Code Smell 61 - Coupling to Classes
Code Smell 135 - Interfaces With just One Realization
Disclaimer
Code Smells are my opinion.
Credits
Photo by Towfiqu barbhuiya on Unsplash
The best smells are something that's easy to spot and most of time lead you to really interesting problems. Data classes (classes with all data and no behavior) are good examples of this. You look at them and ask yourself what behavior should be in this class.
Martin Fowler
Software Engineering Great Quotes
Code Smell 217 - Empty Implementation
You create empty methods instead of failing
TL;DR: Don't fill in methods to comply
Problems
- Fail Fast Principle Violation
Solutions
- Throw an error indicating implementation is not complete
Context
Creating an empty implementation might seem fine to jump to more interesting problems.
The code left won't fail fast so debugging it will be a bigger problem
Sample Code
Wrong
class MerchantProcessor {
processPayment(amount) {
// no default implementation
}
}
class MockMerchantProcessor extends MerchantProcessor {
processPayment(amount) {
// Empty implementation to comply with the compiler
// Won't do anything
}
}
Right
class MerchantProcessor {
processPayment(amount) {
throw new Error('Should be overridden');
}
}
class MockMerchantProcessor extends MerchantProcessor {
processPayment(amount) {
throw new Error('Will be implemented when needed');
}
}
// or better...
class MockMerchantProcessor extends MerchantProcessor {
processPayment(amount) {
console.log('Mock payment processed: $${amount}');
}
}
Detection
- [x]Manual
Since empty code is valid sometimes only a good peer review will find these problems.
Tags
- Hierarchies
Conclusion
Being lazy and deferring certain decisions is acceptable, but it's crucial to be explicit about it.
Relations
Code Smell 30 - Mocking Business
More Info
Credits
Photo by Joey Kyber on Unsplash
There is an art to knowing where things should be checked and making sure that the program fails fast if you make a mistake. That kind of choosing is part of the art of simplification.
Ward Cunningham
Code Smell 218 - Magic Concatenation
If you miss a comma, you are concatenating
TL;DR: Watch out for fancy language assumptions
Problems
- Possible defects
- The least surprise principle violation
- Hidden Assumptions
Solutions
- Be declarative
- Use good linters
- Prefer declarative languages
Context
Many programming languages favor laziness over readability and clean code.
You should use them with caution and trust your tests.
Sample Code
Wrong
tools = [
"Amazon Codewhisperer",
"Bard" # Notice the missing comma
"ChatGPT",
"Dalle-E" # Also here
"Eliza"
]
print(len(tools))
# This will output 3
print(tools)
# ['Amazon Codewhisperer', 'BardChatGPT', 'Dalle-EEliza']
# Missing Commas act as hidden string concatenators
Right
tools = [
"Amazon Codewhisperer",
"Bard",
"ChatGPT",
"Dalle-E",
"Eliza"
]
# We added all the missing commas
print(len(tools))
# 5
print(tools)
# ['Amazon Codewhisperer', 'Bard', 'ChatGPT', 'Dalle-E', 'Eliza']
Detection
- [x]Semi-Automatic
Many linters warn about this problem.
Also, ChatGPT and Bard can detect the problem.
Tags
- Readability
Conclusion
Many modern programming languages come with a significant amount of accidental complexity.
They are often optimized for writing code quickly, even though they may be prone to defects.
Unfortunately, when working with these languages, it is essential to exercise extreme caution.
Relations
Code Smell 84 - Max < Min (Javascript)
Code Smell 69 - Big Bang (JavaScript Ridiculous Castings)
Code Smell 06 - Too Clever Programmer
Credits
Photo by Edge2Edge Media on Unsplash
A programming language is low level when its programs require attention to the irrelevant.
Alan J. Perlis
Code Smell 219 -Looping from index 0
Counting from zero seems natural. Doesn't it?
TL;DR: Start counting from one instead of zero. Like humans do.
Problems
- Bijection from real-world broken
- Cognitive load
- Overly implementation-oriented code
Solutions
- Favor high-level declarative languages
Context
Low-level languages force you to think at a machine level.
Hardware turning machines were designed to use binary gates and start indexing at 0.
A few programming languages use one-based indexing, where indexing starts from 1.
These languages are known for being higher level and more declarative:
- Basic / Visual Basic
- Pascal
- Smalltalk
- Fortran
- Lua
- MATLAB
- R
- Julia
Sample Code
Wrong
package main
import "fmt"
func main() {
numbers := []int{10, 20, 30, 40, 50}
for i := 0; i < len(numbers); i++ {
// Iteration goes from zero to len-1
fmt.Println(numbers[i])
}
}
Right
numbers = [10, 20, 30, 40, 50];
% Looping through the array using one-based indexing
% from 1 to length
for currentIndex = 1:length(numbers)
disp(numbers(currentIndex));
end
Detection
- [x]Automatic
This is a language smell.
Exceptions
- Low-level optimized code
Tags
- Declarative Code
Conclusion
We need to think as humans when we code and not as machines.
Humans count from one.
Zero number was a brilliant discovery in math and science but it does not apply to everyday counting.
Relations
Code Smell 53 - Explicit Iteration
Code Smell 123 - Mixed 'What' and 'How'
More Info
Credits
Photo by Andy Kelly on Unsplash
Pay attention to zeros. If there is a zero, someone will divide by it.
Cem Kaner
Code Smell 220 - Return Tuple
You need to return more than one object
TL;DR: Don't return multiple values.
Problems
- Coupling
- Missing Abstraction
- Readability
- Extensibility
Solutions
- Create a return object grouping the tuple
- Reify it into an object with cohesion and behavior (neither a DTO nor a Dictionary)
- Look for the object in the real world using the MAPPER
- Try to return void and delegate the solution to the modified object avoiding accidental mutations
Context
A function returning multiple values in languages that allow it is a problem.
Developers can use this hack to avoid reifying concepts.
Some languages are: C#, Javascript, Go, Lua, Matlab, PHP, Python, Rust, and Swift
Sample Code
Wrong
func getNameAndAge() -> (String, Int) {
let name = "John"
let age = 30
return (name, age)
}
Right
struct PeopleProfile {
let name: String
let age: Int
}
// You reify the PeopleProfile object
func getNameAndAge() -> PeopleProfile {
let name = "John"
let age = 30
let profile = PeopleProfile(name: name, age: age)
return profile
}
Detection
- [x]Automatic
This is a language smell.
We can tell our linters to warn us.
Tags
- Coupling
Conclusion
This is yet another language feature that hinders clean code and blinds us from seeing missing abstractions in the Bijection.
Relations
Code Smell 10 - Too Many Arguments
Code Smell 122 - Primitive Obsession
Code Smell 27 - Associative Arrays
More Info
Credits
Photo by Edgar Soto on Unsplash
By relieving the brain of all unnecessary work, a good notation sets it free to concentrate on more advanced problems, and in effect increases the mental power of the race. Before the introduction of the Arabic notation, multiplication was difficult, and division even of integers called into play the highest mathematical faculties. Our modern power of easy reckoning with decimal fractions is the almost miraculous result of the gradual discovery of a perfect notation.
Alfred North Whitehead
Next week, 5 more smells