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.)
You can find all the previous code smells (Part I - XLIV) here.
Let's continue...
You abuse cases in switches and make subtle mistakes
TL;DR: Cases are GOTOs, but you might be missing them
In a switch statement, when a match is found in a particular case, the code execution will start from that case and continue executing all subsequent cases until a break statement is encountered or the switch block ends.
This behavior is called "fall-through."
Forgetting a break clause will continue with the following case.
This is similar to a very serious vulnerability that implied an urgent release.
switch (number) {
case 1:
printf("Number is 1.\n");
break;
case 2:
printf("Number is 2.\n");
// Missing break
case 3:
// Case 2 will continue here
printf("Number is 3.\n");
break;
default:
printf("Number is not 1, 2, or 3.\n");
}
// If the number is 2 this will output numbers 2 and 3
switch (number) {
case 1:
printf("Number is 1.\n");
break;
case 2:
printf("Number is 2.\n");
break; // Added 'break' to prevent fall-through
case 3:
printf("Number is 3.\n");
break;
default:
printf("Number is not 1, 2, or 3.\n");
}
// This is correct even though switches AND defaults
// Are other code smells
Many linters and also ChatGPT detect this smell.
Using switches and causes is problematic, your need to use higher-level sentences.
Code Smell 110 - Switches With Defaults
Code Smell 36 - Switch/case/elseif/else/if statements
How to Get Rid of Annoying IFs Forever
Disclaimer: Code Smells are my opinion.
Credits: Photo by Nikola Johnny Mirkovic on Unsplash
I am not terribly dogmatical about the goto statement. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!
Don't abuse this fancy operator
TL;DR: Use comma operator just for loops
In JavaScript, the comma operator allows you to evaluate multiple expressions sequentially and return the value of the last expression.
It's denoted by a comma and separates multiple expressions within a larger expression.
Each expression is evaluated in order from left to right, and the final value of the entire comma-separated expression is the value of the last expression.
const gravitationalConstant = 6.67430e-11;
const massBlackHole1 = 1.5e31; // Mass of the first black hole in kg
const massBlackHole2 = 2.2e32; // Mass of the second black hole in kg
const distanceBlackHoles = 5.7e20; // Distance between black holes in meters
var force = (distanceSquared = distanceBlackHoles * distanceBlackHoles,
(gravitationalConstant * massBlackHole1 * massBlackHole2) /
distanceSquared);
// Two operations in a single statement with comma operator
function calculateGravitationalForce(mass1, mass2, distance) {
const gravitationalConstant = 6.67430e-11;
return (gravitationalConstant * mass1 * mass2) / (distance * distance);
}
const massBlackHole1 = 1.5e31; // Mass of the first black hole in kg
const massBlackHole2 = 2.2e32; // Mass of the second black hole in kg
const distanceBlackHoles = 5.7e20; // Distance between black holes in meters
const force = calculateGravitationalForce(
massBlackHole1,
massBlackHole2,
distanceBlackHoles
);
// Notice force is calculated with a separate function
Many linters can detect this problem.
This valid operator was designed to shorten for loops but is now sometimes abused.
Relations Code Smell 53 - Explicit Iteration
Credits Photo by Stephen Hickman on Unsplash
My computer's so fast it finishes an infinite loop in 5 minutes.
- Chisel Wright
Software evolves, and so does culture.
TL;DR: Avoid old terms like whitelists, blacklists, master, etc.
Language evolves, and technical terms should follow it.
You can change racial names with alternative terminology:
val whitelist = listOf("Barbie", "Ken")
val blacklist = listOf("Midge")
val gitCommand = "git pull origin master"
val process = Runtime.getRuntime().exec(gitCommand)
val allowlist = listOf("Barbie", "Ken")
val denylist = listOf("Midge")
val gitCommand = "git pull origin main"
val process = Runtime.getRuntime().exec(gitCommand)
You can set up a denylist (not a blacklist) of terms you need to double-check for accuracy.
Just as we refactor code to enhance its quality, we should also refactor our language and terminology to promote inclusivity and diversity.
By eliminating racially insensitive terms like "whitelist" and "blacklist" in favor of more inclusive alternatives, we contribute to a more equitable and welcoming tech industry. Let's embrace change and create a coding environment where everyone feels valued, regardless of their background or ethnicity.
Code Smell 105 - Comedian Methods
Prevalence of racist language in discussions of predatory publishing
Credits: Photo by Aarón Blanco Tejedor on Unsplash
An ultimate joint challenge for the biological and the computational sciences is the understanding of the mechanisms of the human brain, and its relationship with the human mind.
- Tony Hoare
You use nice words to excuse bad code
TL;DR: Don't excuse bad code. Write a clean one!
The term comes from Martin Fowler's book "Refactoring: Improving the Design of Existing Code"
# This is a function that adds two numbers
def s(a, b):
# Now you are going to add a and b
res = a + b
# And return the result
return res
def sum(adder, anotherAdder):
return adder + anotherAdder
If you ask ChatGPT to improve this version it will actually worsen it:
def calculate_sum(number1, number2):
# Calculate the sum of two numbers
result = number1 + number2
return result
# In this improved version:
#
# The function name calculate_sum is more descriptive than sum,
# making it clear that this function calculates the sum of two numbers.
# (Wrong) it is more imperative and mistakes the 'what' with the 'how'
#
# The parameter names number1 and number2 are more meaningful
# than adder and anotherAdder, helping to indicate the purpose of each parameter.
# (wrong) They indicate type instead of role
#
# The comment # Calculate the sum of two numbers provides a clear
# and concise explanation of what the function does,
# making it easier for someone reading the code to understand its purpose.
# (wrong) in fact, it is an example of deodorant and useless comment
Most comments are code smells.
You can remove deodorant comments and improve the code.
Remove any meaningless comment you find in your code.
Code Smell 151 - Commented Code
Code Smell 183 - Obsolete Comments
Code Smell 146 - Getter Comments
Code Smell 05 - Comment Abusers
Refactoring 011 - Replace Comments with Tests
Credits: Photo by Ana Essentiels on Unsplash
The reason we mention comments here is that comments often are used as a deodorant. It's surprising how often you look at thickly commented code and notice that the comments are there because the code is bad.
- Martin Fowler
Pass by copy, pass by reference. which is better?
TL;DR: Beware of passing arguments by reference
5 Use Pure Functions
A call-by-reference language like C# or PHP makes it more difficult for a programmer to track the effects of a function call and may introduce subtle bugs. This is a very old technique present in low-level languages to favor performance and avoid the cost of copying large structures.
Some languages like Go use pass-by-value semantics. When you pass arguments to a function, copies are made. However, when you pass a pointer to an object, you can modify the original object within the function. This is another code smell. On the contrary, functional languages forbid this mechanism completely.
using System;
namespace Example
{
class Betelgeuse
{
static void Main(string[] args)
{
double starSize = 100.0;
Console.WriteLine("star size: {0}", starSize);
// star size: 100
double supernovaSize = SimulateFinalSize(ref starSize);
// Notice 'ref' modifier
Console.WriteLine("supernova size: {0}", supernovaSize);
// supernova size: 10000
Console.WriteLine("original star size after: {0}", starSize);
// original star size after: 10000
// WRONG: It should not be affected
}
public static double SimulateFinalSize(ref double size)
{
// Notice 'ref' modifier
// Oversimplification
// You should use Sedov-Taylor solution
size = size * 100;
return size;
}
}
}
using System;
namespace Example
{
class Betelgeuse
{
static void Main(string[] args)
{
const double starSize = 100.0;
// The const modifier warns the compiler
Console.WriteLine("star size: {0}", starSize);
// star size: 100
double supernovaSize = SimulateFinalSize(starSize);
// Notice 'ref' is omitted
Console.WriteLine("supernova size: {0}", supernovaSize);
// supernova size: 10000
Console.WriteLine("original star size after: {0}", starSize);
// original star size after: 100
// It remains at the original value
}
public static double SimulateFinalSize(double size)
{
// Notice 'ref' is omitted
// Oversimplification
// You should use Sedov-Taylor solution
size = size * 100;
return size;
}
}
}
You can use many linters to warn with arguments passed by reference
Passing objects by reference can lead to unexpected side effects if the function modifies the object in a way that wasn't anticipated by the caller.
You should use copy by value instead.
Code Smell 116 - Variables Declared With 'var'
Code Smell 176 - Changes in Essence
Make it correct, make it clear, make it concise, make it fast. In that order.
Wes Dyer
Next week, 5 more smells