paint-brush
How to Find the Stinky Parts of Your Code [Part XLIV]by@mcsee
292 reads

How to Find the Stinky Parts of Your Code [Part XLIV]

by Maximiliano ContieriOctober 4th, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

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.)

People Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - How to Find the Stinky Parts of Your Code [Part XLIV]
Maximiliano Contieri HackerNoon profile picture

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

  1. Split the interface
  2. 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

  1. 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

Code Smell 114 - Empty Class

More Info

Fail Fast

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

Solutions

  1. Be declarative
  2. Use good linters
  3. 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

  1. 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

Wikipedia

Dijkstra on Numbering

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

  1. Create a return object grouping the tuple
  2. Reify it into an object with cohesion and behavior (neither a DTO nor a Dictionary)
  3. Look for the object in the real world using the MAPPER
  4. 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 40 - DTOs

Code Smell 27 - Associative Arrays

More Info

Value Tuples in C#

Named Tuples in Python

Multiple Return Values in Go

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