It 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.)
You can find all the previous code smells (Part i - XXXVII) here.
Let's continue...
You are FTX and your code allows special cases
TL;DR: Don't add hard business rules to your code.
According to Reuters, in a recent FTX scandal, there was a hardcoded condition to skip risk controls to its own portfolio.
The code was explicit and developers were aware of that rule.
if (currentExposure > 0.15 && customer != "Alameda") {
// Be extra careful not to liquidate
liquidatePosition();
}
customer.liquidatePositionIfNecessary(0.15);
// This follows the Tell, Don't ask principle
We can search for primary hardcoded conditions (related to primitive types).
We might have more false positives than actual problems.
If you make code reviews, pay special attention to this kind of hard coding.
Code Smell 133 - Hardcoded IF Conditions
Code Smell 29 - Settings / Configs
Photo by Alexander Mils on Unsplash
Computer science inverts the normal. In normal science, you're given a world, and your job is to find out the rules. In computer science, you give the computer the rules, and it creates the world.
Alan Kay
Software Engineering Great Quotes
The first thing we read after the if the condition is the IF
TL;DR: You have the important else condition on the else.
It is not as straightforward as it appears to write IF clauses in an elegant manner.
There are lots of variants and choices.
We need to pay special attention to readability.
fun addToCart(item: Any) {
if (!cartExists()) {
// Condition is negated
this.createCart();
this.cart.addItem(Item);
// Repeated Code
}
else {
// Normal case is on the else clause
this.cart.addItem(Item);
}
}
fun addToCart(item: Any) {
if (cartExists()) {
this.cart.addItem(Item);
}
else {
this.createCart();
this.cart.addItem(Item);
}
}
fun addToCartShorter(item: Any) {
if (!cartExists()) {
this.createCart();
}
this.cart.addItem(Item);
}
We can find negated expressions on IF conditions and check for this anti-pattern.
We need to read code like prose.
Humans read the standard case first and the exceptional one after it.
Code Smell 51 - Double Negatives
Code Smell 156 - Implicit Else
Photo by Karol Kasanicky on Unsplash
Beauty is more important in computing than anywhere else in technology because software is so complicated. Beauty is the ultimate defense against complexity.
D. Gelernter
Use contextual and local names
TL;DR: Don't repeat your parameters' names. Names should be contextual.
When using names, we often miss that words are contextual and need to be read as a whole sentence.
class Employee
def initialize(@employee_first_name : String, @employee_last_name : String, @employee_birthdate : Time)
end
end
class Employee
def initialize(@first_name : String, @last_name : String, @birthdate : Time)
end
end
We can check our parameter names and try to find duplication.
Use short and contextual names for your parameters.
Code Smell 174 - Class Name in Attributes
Code Smell 87 - Inconsistent Parameters Sorting
Photo by Wolfgang Hasselmann on Unsplash
As a rule, software systems do not work well until they have been used, and have failed repeatedly, in real applications.
David Parnas
Bad actors are there. We need to be very careful with their input.
TL;DR: Sanitize everything that comes from outside your control.
Whenever you get input from an external resource, a security principle requests you to validate and check for potentially harmful inputs.
SQL Injection is a notable example of a threat.
We can also add assertions and invariants to our inputs.
Even better, we can work with Domain Restricted Objects.
user_input = "abc123!@#"
# This content might not be very safe if we expect just alphanumeric characters
import re
def sanitize(string):
# Remove any characters that are not letters or numbers
sanitized_string = re.sub(r'[^a-zA-Z0-9]', '', string)
return sanitized_string
user_input = "abc123!@#"
print(sanitize(user_input)) # Output: "abc123"
We can statically check all the inputs and also we can also use penetration testing tools.
We need to be very cautious with the inputs beyond our control.
Code Smell 121 - String Validations
Code Smell 178 - Subsets Violation
Code Smell 15 - Missed Preconditions
Photo by Jess Zoerb on Unsplash
Companies should make their own enterprise systems as often as network security companies should manufacture their own aspirin.
Phil Simon
Stop thinking of data as attributes. They are only needed to back your behavior
TL;DR: Don't focus on accidental properties. You won't need many of them.
Whenever they want to model a person or an employee, junior programmers or students add an attribute 'id' or 'name' without thinking if they are really going to need them.
We need to add attributes 'on-demand' when there's enough evidence. Objects are not 'data holders'.
class PersonInQueue
attr_accessor :name, :job
def initialize(name, job)
@name = name
@job = job
end
end
class PersonInQueue
def moveForwardOnePosition
# implement protocol
end
end
We can detect unused attributes.
But in many cases, we need an excellent designer to validate the actual need.
Start designing your objects from the protocol.
Add attributes only when needed.
Code Smell 144 - Fungible Objects
Code Smell 109 - Automatic Properties
Photo by Melanie Pongratz on Unsplash
Object thinking focuses our attention on the problem space rather than the solution space.
David West
5 more code smells are coming soon…