How to Find The Stinky Parts of Your Code [Part XII]

Written by mcsee | Published 2021/10/05
Tech Story Tags: code-smells | common-code-smells | clean-code | refactoring | refactor-legacy-code | pixel-face | programming | hackernoon-top-story | web-monetization

TLDR There are 56 code smells that make us doubt the quality of our development. Most of these smells are just hints of something that might be wrong. We want our code to behave different on different environments, operating systems, so taking decisions at compile time is the best decision, isn't it? We need clean code and we must leave premature optimization buried in the past. We need to remove all compiler directives, model it with objects, make a serious benchmark instead of doing premature optimization. This technique was used when CPU were scarce, and nowadays, we need to leave it buried in past.via the TL;DR App

Yet more code smells? More? Where do they come from?

We see several symptoms and situations that make us doubt the quality of our development.

Let's look at some possible solutions.

Most of these smells are just hints of something that might be wrong. They are not rigid rules.

Previous Parts

Part I

Part II

Part III

Part IV

Part V

Part VI

Part VII

Part VIII

Part IX

Part X

Part XI

Let's continue...

Code Smell 56 - Preprocessors

We want our code to behave different on different environments, operating systems, so taking decisions at compile time is the best decision, isn't it?.

Problems

  • Readability
  • Premature Optimization
  • Unnecessary complexity
  • Debugging

Solutions

  1. Remove all compiler directives.
  2. If you want different behavior, model it with objects
  3. If you think there's a performance penalty, make a serious benchmark instead of doing premature optimization.

Sample Code

Wrong

#if VERBOSE >= 2
  printf("trace message");
#endif

Right

if (runtimeEnvironment->traceDebug()){
  printf("trace message");
}

## even better with polymorphism and we avoid annoying ifs

runtimeEnvironment->traceDebug("trace message");

Detection

This is a syntactic directive promoted by several languages, therefore it is easy to detect and replace with real behavior.

Tags

  • Compilers
  • Metaprogramming

Conclusion

Adding an extra layer of complexity makes debugging very difficult. This technique was used when memory and CPU were scarce. Nowadays, we need clean code, and we must leave premature optimization buried in the past.

Bjarne Stroustrup, in his book The Design and Evolution of C++, regrets on the pre-processor directives he created years before.

More info


C++ is designed to allow you to express ideas, but if you don't have ideas or don't have any clue about how to express them, C++ doesn't offer much help.

Bjarne Stroustrup


Code Smell 57 - Versioned Functions

sort, sortOld, sort20210117, workingSort, It is great to have them all. Just in case

Problems

  • Readability
  • Maintainability

Solutions

  1. Keep just one working version of your artefact (class, method, attribute).
  2. Leave time control to your version control system.

Sample Code

Wrong

findMatch()
findMatch_new()
findMatch_newer()
findMatch_newest()
findMatch_version2()
findMatch_old()
findMatch_working()
findMatch_for_real()
findMatch_20200229()
findMatch_thisoneisnewer()
findMatch_themostnewestone()
findMatch_thisisit()
findMatch_thisisit_for_real()

Right

findMatch()

Detection

We can add automatic rules to find versioned methods with patterns.

Like many other patters we might create an internal policy and communicate.

Tags

  • Versioning

Conclusion

Time and code evolution management is always present in software development. Luckily nowadays we have mature tools to address this problem.

Credits

Original idea


That's why I write, because life never works except in retrospect. You can't control life, at least you can control your version.

Chuck Palahniuk


Code Smell 58 - Yo-yo Problem

Searching for a concrete method implementation? Go back and forth, up and down.

TL;DR: Don't ab(use) hierarchies.

Problems

  • Deep Hierarchies
  • Subclassification for Code Reuse
  • Readability
  • Low Cohesion
  • High Coupling

Solutions

  1. Favor composition over inheritance.
  2. Refactor deep hierarchies.

Sample Code

Wrong

<?

abstract class Controller {

}

class BaseController extends Controller {

}

class SimpleController extends BaseController {

}

class ControllerBase extends SimpleController {

}

class LoggedController extends ControllerBase {

}

class RealController extends LoggedController {

}

Right

<?

interface ControllerInterface {
  
}

abstract class Controller implements ControllerInterface {

}

final class LoggedControllerDecorator implements ControllerInterface {

}

final class RealController implements ControllerInterface {

}

Detection

Any linter can check for suspects against a max depth threshold.

Tags

  • Hierarchy

Conclusion

Many novice programmers reuse code through hierarchies. This brings high coupled and low cohesive hierarchies.

Johnson and Foote established in their paper this was actually a good design recipe back in 1988. We have learned a lot from there.

We must refactor and flatten those classes.

More info


An error arises from treating object variables (instance variables) as if they were data attributes and then creating your hierarchy based on shared attributes. Always create hierarchies based on shared behaviors, side.

David West


Code Smell 59 - Basic / Do Functions

sort, doSort, basicSort, doBasicSort, primitiveSort, superBasicPrimitiveSort, who does the real work?

TL;DR: Shortcuts for mini wrappers shout for better solutions.

Problems

  • Readability
  • Bad Naming
  • Low Cohesion
  • Single Responsibility Principle

Solutions

  1. Use good object wrappers
  2. Use dynamic decorators

Sample Code

Wrong

<?

final class Calculator {

    private $cachedResults;

    function computeSomething() {
        if (isset($this->cachedResults)) {
            return $this->cachedResults;
        }
        $this->cachedResults = $this->logAndComputeSomething();
    }

    private function logAndComputeSomething() {
        $this->logProcessStart();
        $result = $this->basicComputeSomething();
        $this->logProcessEnd();
        return $result;
    }

    private function basicComputeSomething() {
        /// Do Real work here
    }

}

Right

<?

final class Calculator {
    function computeSomething() {
        // Do Real work here since I am Compute!
    }
}

//Clean and cohesive class, single responsibility

final class CalculatorDecoratorCache {

    private $cachedResults;
    private $decorated;

    function computeSomething() {
        if (isset($this->cachedResults)) {
            return $this->cachedResults;
        }
        $this->cachedResults = $this->decorated->computeSomething();
    }
}

final class CalculatorDecoratorLogger {

    private $decorated;

    function computeSomething() {
        $this->logProcessStart();
        $result = $this->decorated->computeSomething();
        $this->logProcessEnd();
        return $result;
    }
}

Detection

We can instruct our static linters to find wrapping methods if they follow conventions like doXXX(), basicXX() etc.

Tags

  • Declarativiness

Conclusion

We came across this kind of methods some time in our developer life, We smelled something was not OK with them. Now is the time to change them!

More info


The primary disadvantage of Wrap Method is that it can lead to poor names. In the previous example, we renamed the pay method dispatchPay() just because we needed a different name for code in the original method.

Michael Feathers


Code Smell 60 - Global Classes

Classes are handy. We can call them and invoke them any time. Is this good?

TL;DR: Don't use your classes as a global point of access.

Problems

  • Coupling
  • Classes are global unless we use Namespaces.
  • Name polluting
  • Static Methods
  • Static Constants
  • Singletons

Solutions

  1. Use namespaces, module qualifiers or similar
  2. To avoid namespace polluting, keep the Global names as short as possible.
  3. Class single Responsibility is to create instances.

Sample Code

Wrong

<?

final class StringUtilHelper {
    static function reformatYYYYDDMMtoYYYYMMDD($date) {
    }
}

class Singleton {

}

final class DatabaseAccessor extends Singleton {

}

Right

<?

namespace Date;

final class DateFormatter {

    function reformatYYYYDDMMtoYYYYMMDD(Date $date) {
    }
    //function is not static since class single responsibility is to create instances and not be a library of utils

}

namespace OracleDatabase;

class DatabaseAccessor {
    //Database is not a singleton and it is namespace scoped
}

Detection

We can use almost any linter or create dependency rules searching for bad class references.

Tags

  • Globals

Conclusion

We should restrict our classes to small domains and expose just facades to the outside. This greatly reduces coupling.

More info

Write shy code — modules that don't reveal anything unnecessary to other modules and that don't rely on other modules' implementations.

Dave Thomas


We are done for some time (not many).

But we are pretty sure we will come across even more smells very soon!

I keep getting more suggestions on twitter, so they won't be the last!

Send me your own!


Written by mcsee | I’m senior software engineer specialized in declarative designs and S.O.L.I.D. and Agile lover.
Published by HackerNoon on 2021/10/05