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

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

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

Too Long; Didn't Read

Finding the stinky parts of your code--debugging and programming help for software developers.

People Mentioned

Mention Thumbnail

Company Mentioned

Mention Thumbnail
featured image - How to Find the Stinky Parts of Your Code [Part XLVII]
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 - XLVI) here.


Let's continue...


Code Smell 231 - Redundant Data

Where are your sources of truth?

TL;DR: Say it only once

Problems

  • Don't Repeat Yourself principle violation
  • Consistency problems
  • Maintainability
  • Testing and Debugging

Solutions

  1. Keep the responsibilities to relevant objects and delegate to a single source of truth.

Context

The principle of "Don't Repeat Yourself" (DRY) encourages you to avoid redundancy and duplication of behavior.

Redundant data can lead to inconsistencies because updates or changes need to be made in multiple places.

If you update one instance of the data and forget to update another, your system can become inconsistent, which can lead to errors and unexpected behavior.

Maintaining redundant data can be a nightmare when it comes to making changes or updates since It increases the workload and the likelihood of introducing errors during maintenance.

With a single source of truth, you only need to make changes in one place, simplifying the maintenance process.

When data is repeated in multiple places, it becomes difficult to identify the authoritative source of that data, leading to confusion for developers.

Sample Code

Wrong

class Transfer:
    def __init__(self, amount, income, expense):
        self.amount = amount
        self.income = income
        self.expense = expense

class Income:
    def __init__(self, amount):
        self.amount = amount
        # amount is the same for party and counterparty

class Expense:
    def __init__(self, amount):
        self.amount = amount

transfer_amount = 1000  
# simplification: should be a money object with the currency

income = Income(transfer_amount)
expense = Expense(transfer_amount)
transfer = Transfer(transfer_amount, income, expense)

print("Transfer amount:", transfer.amount)
print("Income amount:", transfer.income.amount)
print("Expense amount:", transfer.expense.amount)

Right

class Transfer:
    def __init__(self, amount):
        self.amount = amount
        self.income = Income(self)
        self.expense = Expense(self)

class Income:
    def __init__(self, transfer):
        self.transfer = transfer

    def get_amount(self):
        return self.transfer.amount

class Expense:
    def __init__(self, transfer):
        self.transfer = transfer

    def get_amount(self):
        return self.transfer.amount

transfer_amount = 1000  
transfer = Transfer(transfer_amount)

print("Transfer amount:", transfer.amount)
print("Income amount:", transfer.income.get_amount())
print("Expense amount:", transfer.expense.get_amount())

Detection

  • [x]Manual

This is a semantic smell

Exceptions

  • For performance issues, you can add caches and redundancy, but you need extra effort to keep the data synchronized

Tags

  • Data

Conclusion

In larger and more complex systems, redundancy becomes a significant problem.

As your system grows, the challenges associated with maintaining and synchronizing redundant data also increase.

Redundant data also increases the surface area for testing and debugging.

You need to ensure that all copies of the data behave consistently, which can be a challenging task.

Relations

Code Smell 49 - Caches

Disclaimer

Code Smells are my opinion.

Credits

Photo by Jørgen Håland on Unsplash


Everything will ultimately fail. Hardware is fallible, so we add redundancy. This allows us to survive individuals hardware failures, but increases the likelihood of having at least one failure at any given time.

Michael Nygard


Code Smell 232 - Reusable Code


Don't Repeat Yourself. Don't Repeat Yourself

TL;DR: You can find missing abstractions by looking at repeated code

Problems

  • DRY principle violation
  • Maintainability
  • Ripple Effect

Solutions

  1. Create the repeated code
  2. Introduce an abstraction
  3. Replace the references and point to the new abstraction
  4. Remove the duplication

Context

Repeated code is a symptom of missing abstractions.

This is natural in the learning process since we cannot foresee those abstractions.

Sample Code

Wrong

def calculate_area(length, width):
    return length * width

def calculate_volume(length, width, height):
    return length * width * height

Right

def calculate_area(length, width):
    return length * width

def calculate_volume(length, width, height):
    base_area = calculate_area(length, width)
    return base_area * height

Detection

  • [x]Semi-Automatic

Some linters can find repeated code patterns.

Exceptions

The abstraction must have a dependency correspondence on the Bijection

Tags

  • Bloaters

Conclusion

Repeated code is a problem and a hint for a missing abstraction.

Remember you don't need to avoid copying and pasting.

You must explicitly write the repeated code and remove the duplication by introducing an abstraction.

Avoiding the cut and paste is a shortcut and a symptom of premature optimization.

Relations

Code Smell 46 - Repeated Code

Code Smell 182 - Over Generalization

Credits

Photo by Mitchell Griest on Unsplash


Pasting code from the internet into production code is like chewing gum found in the street.

Mike Johnson


Code Smell 233 - Collections Count

You count collections or collections.count?

TL;DR: Chose narrow names

Problems

  • Bad Naming

Solutions

  1. Accurately describe your collections

Context

Names are significant and should not deceive the reader. f You name things and lose the scope of the name.

It is important to be accurate of the expected reference on the names.

Sample Code

Wrong

const standardModelParticles = {
  quarks: [
    {
      name: "Up",
      charge: "2/3",
      type: "Quark",
    },
    {
      name: "Down",
      charge: "-1/3",
      type: "Quark",
    },
    // ...
  ],
  leptons: [
    {
      name: "Electron",
      charge: "-1",
      type: "Lepton",
    },
    {
      name: "Muon",
      charge: "-1",
      type: "Lepton",
    },
    // ...
  ],
  gaugeBosons: [
    {
      name: "Photon",
      charge: "0",
      type: "Boson",
    },
    {
      name: "W Boson",
      charge: "±1",
      type: "Boson",
    },
    // ...
  ],
  higgsBoson: [
    {
      name: "Higgs Boson",
      charge: "0",
      type: "Scalar Boson",
    },
  ],
};
 
const quarks = standardModelParticles.quarks.length; 
// Bad name. It does not represent a count
// But a Collection of things

Right

const standardModelParticles = {
}; // Same as the "Wrong" example
 
const quarksCount = standardModelParticles.quarks.length; 

Detection

  • [x]Semi-Automatic

Some linters can check the types and names and infer a mistake

Tags

  • Namings

Conclusion

Take care of your names.

Use automatic refactor tools whenever you come across a bad name.

Relations

Code Smell 163 - Collection in Name

Code Smell 134 - Specialized Business Collections

Code Smell 33 - Abbreviations

Credits

Photo by Sandy Millar on Unsplash


Some people are good programmers because they can handle many more details than most people. But there are a lot of disadvantages in selecting programmers for that reason - it can result in programs that no one else can maintain.

Butler Lampson


Code Smell 234 - Long Circuit

Be smart (and lazy) with low performant conditions

TL;DR: Premature Optimization is Evil. Optimization is Good.

Problems

  • Low Performance

Solutions

  1. Sort the conditions from faster to slower

Context

Readability is always essential and you should avoid premature optimization.

Non-premature optimization happens when you have actual evidence you can improve your code execution time without much readability penalizations.

Sample Code

Wrong

def is_warm():
    # This is a fast api call to your thermometer
    response = requests.get
        ("https://iot-device-api.example.com/current_temperature")
    temperature_data = response.json()
    
    return temperature_data.get('temperature', 0) > 25  
    
def is_weekend():
    # This function checks if today is a weekend
    # based on a slow calendar API call
    response = requests.get
        ("https://calendar-api.example.com/today")
    calendar_data = response.json()
    
    return calendar_data.get('day_of_week', '').lower() 
        in ['saturday', 'sunday']

def is_sunny():
    # Very slow function to a low performant weather API call
    response = requests.get
        ("https://weather-api.example.com/current")
    weather_data = response.json()
    
    return weather_data.get('weather', '') == 'sunny'

is_sunny_value = is_sunny()
is_warm_value = is_warm()
is_weekend_value = is_weekend()  
  
if is_sunny_value and is_warm_value and is_weekend_value:
    # the 3 conditions are always evaluated
    print("Let's go outside!")
else:
    print("Stay at home.")

Right

if is_warm() and is_weekend() and is_sunny():
    # the 3 conditions are evaluated in short circuit 
    # and sorted from fastest to slowest
    # for a fast exit
    print("Let's go outside!")
else:
    print("Stay at home.")

Detection

  • [x]Semi-Automatic

You can detect slow calls using actual benchmarks.

Do not consider algorithm complexity since sometimes it is unrelated to actual data distribution. (for example, optimizing an array with a few elements).

Tags

  • Performance

Conclusion

Find bottlenecks using Pareto rules.

Optimize your code-critical sections.

Relations

Code Smell 140 - Short Circuit Evaluation

Code Smell 145 - Short Circuit Hack

Credits

Photo by Nick Abrams on Unsplash


The key to performance is elegance, not battalions of special cases.

Jon Bentley and Douglas McIlroy


Code Smell 235 - Console Side Effects

You compute and log in the same place

TL;DR: Avoid side effects

Problems

  • Coupling
  • Testability
  • Reusability
  • Function Composition

Solutions

  1. Decouple the code and avoid side-effects
  2. Inject the output destination

Context

Outputting to the console within an internal function generates coupling and side effects

Sample Code

Wrong

Right

Detection

[X] Automatic

Several linters warn for this usage

Tags

- Globals

Conclusion

Instead of logging directly within internal functions, a more modular and flexible approach is to have functions return values or throw exceptions when errors occur.

The calling code can then decide how to handle and log these results based on the application's logging strategy.

Relations

[Code Smell 17 - Global Functions])

Credits

Image generated by Midjourney


Memory is like an orgasm. It's a lot better if you don't have to fake it.

Seymour Cray


Next week, 5 more smells.