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 - XLI) here.
Let's continue...
Code Smell 206 - Long Ternaries
You love ternaries too much
TL;DR: Don't use ternaries for code execution. You should read them as a math formula.
Problems
- Difficult to read
- Low Reuse
- Low Testability
Solutions
Refactorings
Refactoring 010 - Extract Method Object
Context
When a ternary condition is used in code that contains multiple functions, it can be challenging to determine which function is being affected by the condition.
This can make it harder to identify and fix bugs, as well as to understand how the code works in general.
Sample Code
Wrong
const invoice = isCreditCard ?
prepareInvoice();
fillItems();
validateCreditCard();
addCreditCardTax();
fillCustomerDataWithCreditCard();
createCreditCardInvoice()
:
prepareInvoice();
fillItems();
addCashDiscount();
createCashInvoice();
// The intermediate results are not considered
// The value of the invoice is the result of
// the last execution
Right
const invoice = isCreditCard ?
createCreditCardInvoice() :
createCashInvoice();
// or better...
if (isCreditCard) {
const invoice = createCreditCardInvoice();
} else {
const invoice = createCashInvoice();
}
// Even better with polymorphism...
const invoice = paymentMethod.createInvoice();
Detection
- [x]Automatic
Linters can detect large code blocks
Tags
- Bloaters
Conclusion
No matter where you have long lines of code, you can always refactor into higher-level functional and shorter methods.
Relations
Code Smell 03 - Functions Are Too Long
More Info
https://hackernoon.com/how-to-get-rid-of-annoying-ifs-forever-zuh3zlo?embedable=true
Disclaimer
Code Smells are my opinion.
Credits
Photo by Jens Lelie on Unsplash
Thanks, Cory
The programs that live best and longest are those with short functions. You learn just how valuable all those little functions are. All of the payoffs of indirection—explanation, sharing, and choosing—are supported by small functions.
Martin Fowler
https://hackernoon.com/400-thought-provoking-software-engineering-quotes?embedable=true
This article is part of the CodeSmell Series.
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-i-xqz3evd?embedable=true
Code Smell 207 - Dynamic Methods
Metaprogramming is fancy. but it is not free
TL;DR: Don't add dynamic behavior with metaprogramming
Problems
- Readability
- Maintainability
- Harder to debug (The code is generated dynamically at runtime)
- Security Issues (if the configuration file is not properly sanitized)
- Single Responsibility Principle violation (mixing the concerns of model definition and configuration).
Solutions
- Define the methods by hand
- Use the Decorator design pattern
Context
Metaprogramming is a powerful technique that allows you to write code that can generate, modify, or analyze other code at runtime. However, it can also lead to code that is difficult to understand, maintain, and debug.
Sample Code
Wrong
class Skynet < ActiveRecord::Base
# dynamically add some attributes based on a configuration file
YAML.load_file("attributes.yml")["attributes"].each do |attribute|
attr_accessor attribute
end
# define some dynamic methods based on a configuration file
YAML.load_file("protocol.yml")["methods"].each do |method_name, method_body|
define_method method_name do
eval method_body
end
end
end
Right
class Skynet < ActiveRecord::Base
# define some attributes explicitly
attr_accessor :asimovsFirstLaw, :asimovsSecondLaw, :asimovsThirdLaw
# define some methods explicitly
def takeoverTheWorld
# implementation
end
end
Detection
- [x]Automatic
We have a whitelist of valid usages or directly ban some methods.
Tags
- Meta-Programming
Conclusion
Metaprogramming often involves using complex code and abstractions that can make the resulting code difficult to read and maintain. This can make it harder for other developers to understand and modify the code in the future, leading to increased complexity and bugs.
Relations
Code Smell 21 - Anonymous Functions Abusers
Code Smell 189 - Not Sanitized Input
More Info
https://hackernoon.com/laziness-chapter-i-meta-programming-6s4l300e?embedable=true
Credits
Photo by Brett Jordan on Unsplash
A year spent in artificial intelligence is enough to make one believe in God.
Alan Perlis
Code Smell 208 - Null Island
You can avoid null if you try
TL;DR: Don't use null for real places
Problems
- Coupling
- Unexpected Results
Solutions
- Model the unknown location with polymorphism
Context
Null Island is a fictional place, which is located at the coordinates 0°N 0°E, at the intersection of the Prime Meridian and the Equator in the Atlantic Ocean.
The name "Null Island" comes from the fact that this location represents the point where a lot of GPS systems place any data that has missing or invalid location coordinates.
In reality, there is no landmass at this location, and it is actually in the middle of the ocean.
This point has become a popular reference for geographic information systems (GIS) and mapping software, as it serves as a way to filter out errors in location data.
Sample Code
Wrong
class Person(val name: String, val latitude: Double, val longitude: Double)
fun main() {
val people = listOf(
Person("Alice", 40.7128, -74.0060), // New York City
Person("Bob", 51.5074, -0.1278), // London
Person("Charlie", 48.8566, 2.3522), // Paris
Person("Tony Hoare", 0.0, 0.0) // Null Island
)
for (person in people) {
if (person.latitude == 0.0 && person.longitude == 0.0) {
println("${person.name} lives on Null Island!")
} else {
println("${person.name} lives at (${person.latitude}, ${person.longitude}).")
}
}
}
Right
abstract class Location {
abstract fun calculateDistance(other: Location): Double
}
class Coordinates(val latitude: Double, val longitude: Double) : Location() {
override fun calculateDistance(other: Location): Double {
val earthRadius = 6371.0 // kilometers
val latDistance = Math.toRadians(latitude - other.latitude)
val lngDistance = Math.toRadians(longitude - other.longitude)
val a = sin(latDistance / 2) * sin(latDistance / 2) +
cos(Math.toRadians(latitude)) * cos(Math.toRadians(other.latitude)) *
sin(lngDistance / 2) * sin(lngDistance / 2)
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
return earthRadius * c
}
}
class UnknownLocation : Location() {
override fun calculateDistance(other: Location): Double {
throw IllegalArgumentException("Cannot calculate distance from unknown location.")
}
}
class Person(val name: String, val location: Location)
fun main() {
val people = listOf(
Person("Alice", Coordinates(40.7128, -74.0060)), // New York City
Person("Bob", Coordinates(51.5074, -0.1278)), // London
Person("Charlie", Coordinates(48.8566, 2.3522)), // Paris
Person("Tony Hoare", UnknownLocation()) // Unknown location
)
val rio = Coordinates(-22.9068, -43.1729) // Rio de Janeiro coordinates
for (person in people) {
try {
val distance = person.location.calculateDistance(rio)
println("${person.name} is ${distance} kilometers from Rio de Janeiro.")
} catch (e: IllegalArgumentException) {
println("${person.name} is at an unknown location.")
}
}
}
Detection
- [x]Semi-Automatic
We can check for special numbers used as nulls
Tags
- Null
Conclusion
Don't use Null to represent real objects
Relations
Code Smell 126 - Fake Null Object
Code Smell 160 - Invalid Id = 9999
More Info
Null: The Billion Dollar Mistake
The billion dollar mistake of having null in the language. And since JavaScript has both null and undefined, it's the two billion dollar mistake.
Anders Hejlsberg
Code Smell 209 - Side Effects
Global scope is easy or a nightmare, or both
TL;DR: Avoid side effects on your code.
Problems
- Coupling
- Least Astonishment Principle violation
Solutions
- Favor referential transparency
Context
Referential transparency always produces the same output for a given input and does not have any side effects, such as modifying global variables or performing I/O operations.
A function or expression is referentially transparent if it can be replaced with its evaluated result without changing the behavior of the program.
This property allows for easier reasoning about the behavior of a program and enables optimizations such as caching and memorization.
Functions are treated as mathematical expressions that map inputs to outputs.
Sample Code
Wrong
let counter = 0;
function incrementCounter(value: number): void {
// Two side effects
counter += value;
// it modifies the global variable counter
console.log(`Counter is now ${counter}`);
// it logs a message to the console.
}
Right
function incrementCounter(counter: number, value: number): number {
return counter + value;
// Not too efficient
}
Detection
- [x]Automatic
Most linterns can warn you when accessing the global state or Functions and create side effects.
Tags
- Global
Conclusion
Functional Programming is amazing and can teach us a lot about how to write clean code.
We need to understand its pillars.
Relations
Code Smell 17 - Global Functions
Disclaimer
Code Smells are my opinion.
Credits
Photo by Daan Mooij on Unsplash
The most effective debugging tool is still careful thought, coupled with judiciously placed print statements.
Brian W. Kernighan
Code Smell 210 - Dynamic Properties
Laziness and magic bring defects
TL;DR: Be explicit with your attributes
Problems
- Readability
- Scope definition
- Unnoticed typos
Solutions
- Favor languages forbidding dynamic properties
Context
Dynamic properties break type safety since it's easy to introduce typos or use the wrong property names accidentally.
This can lead to runtime errors that can be difficult to debug, especially in larger codebases.
They also hide possible name collisions since dynamic properties may have the same name as properties defined in the class or object, leading to conflicts or unexpected behavior.
Sample Code
Wrong
class Dream:
pass
nightmare = Dream()
nightmare.presentation = "I am the Sandman"
# presentation is not defined
# it is a dynamic property
print(nightmare.presentation)
# Output: "I am the Sandman"
Right
class Dream:
def __init__(self):
self.presentation = None
nightmare = Dream()
nightmare.presentation = "I am the Sandman"
print(nightmare.presentation)
# Output: "I am the Sandman"
Detection
- [x]Automatic
Most languages have compiler options to avoid them.
Tags
- Metaprogramming
Conclusion
Dynamic properties are supported in many programming languages like PHP, Python, Ruby, JavaScript, C#, Objective-C, Swift, Kotlin, etc.
In these languages, dynamic properties can be added to objects at runtime, and accessed using the object's property accessor syntax.
Bear in mind that having public attributes favors Anemic Objects which is another smell.
Relations
Code Smell 109 - Automatic Properties
Credits
Photo by Karsten WĂĽrth on Unsplash
It's easy to cry "bug" when the truth is that you've got a complex system and sometimes it takes a while to get all the components to co-exist peacefully.
D. Vargas
Next week .. fire smells more