My Code Stinks and That's OK

An impassioned defense of “code smells” and why tech bloggers have gone insane

My Code Stinks and That's OK

We’ve all seen the articles. You know the ones I’m talking about. Those ones. Simple, reductive headlines like “RECURSION IS BAD” or “WE NEED TO STOP USING IF/ELSE” from people who just discovered functional programming. And look, it's fine to have differing opinions about things, code styles included, but there’s a threshold where you cross from pedantic to dogmatic. And if we’re honest, it's getting a little out of hand. So I decided that someone need to step up and defend the “code smells”, the basic building blocks of how real, actual production code works.

If/Else is OK

I’ve seen way way way too many articles espousing the evils of if/then statements and how there’s always a better way to do things, like switch statements and the like. And yeah, for some things, switch or select is appropriate. In Go, for example, when you want to check if a channel has anything waiting on it you should use a select so as not to block the main thread.

ch := make(chan bool)
select {
	case x <-ch:
    	// do something with the channel
    default:
    	// do something else
}

If you have a value and you want to check that value against a wide number of conditions, then a switch statement might be the best way to go. Performance and readability are better in this case (typically). But what about languages that don’t have a switch or select like Python? Well, you’re kind of stuck with using if/else aren’t you? Could you write or import some kind of library that re-implements switch or does some other kind of evaluation? Sure. But you could just as easily use if. I would argue that this:

def my_function(x):    
	if x == 0:
    	# do something
    elif x == 1:
    	# do something
    else:
    	# catch all

Is just as perfectly readable as this (if not more so):

function my_function(x) {    
    switch(x) {       
        case 0:          
            // do something       
        case 1:
            // do something       
        default:          
            // catch all    
    }
}

Yes, you want to avoid using nested if if it is at all possible, but sometimes you just need to, and that’s ok. The important thing is that your code is readable, followable, and only as complex as it needs to be.

For Loops Are Fundamental to Code, and That’s OK

This is probably my favorite and the real inspiration for this rebuttal. I’ve seen blogs talking about how you shouldn’t use for and instead use functions like forEach and map instead. Which if you know anything about anything, is fairly hilarious because you’re just offloading the for into a function. There’s no magic in those methods that somehow gets around a basic loop. In fact, loops are one of the most fundamental pieces of software design. A programming language isn’t really a programming language without some sort of looping mechanism, otherwise its not Turing complete. And all of this fails to address the simple fact that most languages don’t have a forEach-like function, but all of them have a for or while. Just because it’s good for JavaScript doesn’t mean its good for everything else — most things don’t run on JS (thank god).

In Python you have a few ways of looping, some of them more appropriate than others at various times. Personally I’m a fan of comprehensions but they don’t make for the most readable code, something that you very much have to consider in a setting where the code you write might be maintained or improved by someone other than yourself.

[{x: int(y)**2} for x,y in my_dict.items()]

is the same as

result = []
for x,y in my_dict.items():    
	result.append({x: y**2})

but the second might be easier for your average developer to grasp without having to go diving into the Python 3 docs and doing a crash course in comprehension. The comprehension might be a tad but faster, but CPU these days is cheap and unless you’re doing some kind of real-time clock-time-is-essential type operations, then you can afford to spend an extra millisecond or two and have maintainable code.

Switch Statements are OK Too

But wait, didn’t you just get done saying that using if/else instead of switch is fine. Why are we talking about switch again? Well, because this pile of code exists.

In case you don’t want to risk reading the linked article, the tl;dr is to use a factory instead of switch. Which….ugh. Why? WHY? WHYYYYY?

audible facepalm
Surely this is much easier to read than a switch statement…..right? RIGHT?

Why do we need to even have these conversations? Why, for the love of everything that is holy, would you take a simple switch and implement a complex OOP paradigm using classes and factories? If you’re going to those kinds of extremes to get milliseconds of performance out of JavaScript then maybe it's time to learn a new language, one that isn’t JIT compiled and interpreted, like Rust or Go or C++ or C# or Erlang or…you get my point. Need to run in a browser? WASM has your back.

We’re All OK

I may have poked a little fun and got carried away at times, but don’t we all? I mean, some of the inspirations for this article certainly have. But if there’s one thing I want to hammer home, it's this: code style and convention are all subjective (to a point). If you can’t bring yourself to write factories instead of basic flow control, then by all means, do what you can do. We’re fine. Your code will be fine, you will be fine, and the universe isn’t going to implode from one more if/else. No matter where you’re at in your journey through development — from rookie just learning what a function is to the old greybeards looking for ways to purposefully confuse the interns and write code that was never meant to exist — just do your best, keep learning, and ignore zealots. Because at the end of the day, just like pilots and landings, any application that successfully starts is a good one. I’ll stick to my smelly code.

Anything I missed? Let me know in the comments!