How to Write Bad Code: The Definitive Guide, Part I

Daniel Lempesis
8 min readJul 15, 2018

--

Writing robust, readable code is boring. No one wants to do it, we all just have to do it. Languages like Python have even gone out of their way to try to make it hard to write bad code. Luckily we’ve got languages like Whitespace and BF to aid us in our battle against the evils of writing decent code that others (and our future selves) are capable of understanding, but we don’t always have the happy option of using these unapologetically r̶i̶d̶i̶c̶u̶l̶o̶u̶s̶- cryptic languages.

Be it a project manager or team lead who insists on using bland languages like JavaScript, Python or Ruby, a project that’s already inexplicably written in Java or C#, or friends you’re working on some humanitarian side-project with who selfishly insist on being able to read the code you’ve written, sometimes we just don’t have a choice.

Don’t despair. There are ways to bring JavaScript, Ruby, and yes, even Python, into the noble realm of indecipherability. Let’s make Caesar and Vigenère proud!

(…Or better yet, jealous.)

Kicking it off: Mutating the Input

Any programmer worth his salt (the mineral, not the cryptographic technique) knows that mutating the original input is the first step in the noble journey to making your code both confusing, and likely to behave in unexpected (and thus highly-desirable) ways.

Do you have a critical piece of data that shouldn’t change, and want any function working on it after yours to break or return invalid outputs? Mutate the input. Want to make your coworker wonder what the heck is going on with the app and question whether or not it’s her fault? Mutate the input. Want to make a benchmark utterly meaningless after the first loop? Of course you do! Mutate that input.

Mutate thoroughly, and mutate often. In fact, don’t just alter the input once. If you can, mutate it several times in your function before returning it, changing its type at least once. Most importantly, do not hesitate to frequently declare and then mutate global variables. Your app should basically be a Jenga tower in its final form; any change should make it all come crashing down.

Note that this invaluable tool is sadly extremely difficult in so-called “pure” languages like Haskell, but for traditional languages, there’s no stopping you. Don’t let a single opportunity go to waste, or you run the very real risk of ending up with an occasional bit of reusable code.

Variable Naming: Be Arbitrary, Be Nondescript

Meaningful, descriptive variable names are the bane of any self-respecting codebase. It’s called code for a reason. It’s not supposed to be easily understandable. So if you’ve got functions that frequently look like this:

const approximateHalfCircleArea = radius => {
const area = Math.PI * (radius ** 2)
, halfArea = area/2;
return Math.round(halfArea * 1000) / 1000;
}

…then something’s wrong.

There are several immediately apparent mistakes here:

  1. It’s far too obvious what our function does; one look at its name, and the jig is up.
  2. The parameter name we’ve given our function — radius — is similarly too descriptive.
  3. Lastly, we’ve used a well-known function, Math.round() , which also says exactly what it does right in the name.

These are all bad things. The only win we’ve secured in this function is that it at least uses the ** operator introduced in ES7, which some JS users may not be used to seeing. But that’s a small consolation, given we haven’t so much as changed the type of any of our values even temporarily.

So let’s make things right: let c = r => +(r**2 * 3.1416).toFixed(2)/2

Already we can see a whole host of improvements. Our future selves will have no idea what our function might do just by looking at its name. Its parameter, r, is pretty unhelpful (although admittedly, r does often stand for radius), and we’ve used toFixed() to round into string format before finally converting it back to a float with the plus sign way at the beginning. We’ve arbitrarily rounded pi to four decimals, which most people aren’t used to seeing. We even declared it with let, meaning if we forget about this later in the same scope, we can easily reassign c to something else entirely.

For even better results, use toPrecision(), as it’s slightly less common than toFixed() (which is actually used with some regularity to round floats): c=x=>+(x*x*1.7724**2).toPrecision(4)*.5

Notice the lack of spacing and variable declaration (we’ve now made c a global variable), and the use of the square root of pi rather than pi itself. Isn’t that better?

The beauty of this is that the more code you’ve got, and the more variables you have named like this, the harder it becomes to untangle. Cluttering the global namespace with one-letter variables likely to clash with each other is a valuable skill not to be underestimated in its ability to make your code t̶e̶r̶r̶i̶b̶l̶e̶- great again.

Formatting: Just Don’t.

There’s a famous quote by Mark Twain: “Show me a body who formats his code consistently and properly, and I’ll show you a man who’s trying to compensate for his lack of ability.”

Abraham Lincoln once said something similar (I believe during the Gettysburg Address), although he was more nuanced in his language and sensitive to his potential audience, not sticking solely to masculine pronouns and fully acknowledging that women, too, in occasional bouts of insecurity, are also prone to formatting their code properly.

And you know what? They’re right. Obviously, languages like Python give us limited options in this area, but you can get away with gems like using camelCase, indenting by only two spaces instead of a full tab half the time, and utilizing semicolons to keep multiple statements on one line, so use this to your advantage. (Inappropriate use of lambdas also comes to mind.)

In languages like JavaScript and Ruby, though, everything’s fair game. Indentation is completely optional. You’re free to employ completely inconsistent use of curly braces (and depending on your code, you may not need any at all). Want to declare a function as a default parameter instead of in the body of your function because it takes up less space that way? Go for it! Feel like playing fast and loose with data types, changing them every other line? Do it!

You can even mix and match ES5, ES6 and ES7+ as you please. Declare a function in snake_case as an ES5 variable like this:

var add_one_and_two = (function(v1,v2) { 
return(v1+v2)
})

…and your next function as a global ES6 PascalCase arrow function like this: AddOneAndTwo=(v1,v2)=>v1+v2;

In many languages you may be limited to type conversion in only one or two ways much of the time (e.g. float.to_i and float.floor, in Ruby), but JavaScript often gives you many options to choose from. In this case, you are free to use parseInt() (with or without a radix), +toFixed(0), bitwise operators, Math.floor, Math.trunc, and more. Use whatever way you feel like at any given moment; variety is, after all, the spice of life, and this ensures your code will be rich and varied.

Staying WET in a DRY World

We’re all familiar with the boring, desiccated acronym DRY.

“Don’t Repeat Yourself” it commands, with an insufferable, supercilious air, as though it somehow knows better than we do.

It doesn’t. Harnessing the awesome power of programmatic tautology is part of what makes long, meandering and breakable spaghetti code possible. No one is going to respect you if the code you write can be understood by relatively new coders, or — worse yet — someone brand new to the language you’re writing in. If someone who’s never touched your language before can understand what you’re doing, you’ve failed.

Everyone loves spaghetti. Anyone who claims otherwise is either lying or works for the gluten-free food industry (and is lying). This is where WET comes in: Write Everything Twice.

WET is an extremely important concept when it comes to writing illegible code; in addition to the fact that no one enjoys dry spaghetti, the more times you write the same thing, the bigger (and thus more impressive) your program becomes and the more likely it is to break down completely after making the slightest change to any other portion of it. It also makes it a lot harder to debug, which is the goal.

Why? Job security. If literally anyone can understand your code, you’re replaceable. If only you (and ideally, not even you) can read your code, you’re home free. Show me a programmer whose chimerical code litters his entire workplace’s codebase, and I’ll show you someone too valuable to let go.

To unironically repeat my earlier point: coworkers won’t think much of you if your code isn’t esoteric, hard to work with, and complete with an enormous cognitive burden. How else are they going to know you’re a genius? Write Everything Twice.

I can’t stress that enough.

Method Chaining: Never Stop.

This is a bit of a controversial one. Some languages, like Ruby (and thus, Crystal, e.g.) were designed to work well with chaining; it takes quite a while for chaining functions in pure dot notation to become unreadable. That doesn’t mean you shouldn’t try, just that you ought to be aware that your efforts to obfuscate your code might be better applied elsewhere.

For example, say you’re required to square all the digits of an integer and return the result as an integer (e.g., 12345 => 1491625). In Ruby, this is depressingly readable:

def sq(num)   
num.digits.map(&:abs2).reverse.join.to_i
end

It’s true there are ways to improve the above method (it can be shortened with the use of to_sand gsub, or unnecessarily turned into a lambda), but it’s mostly a lost cause.

In JavaScript, things are a bit better:

const sq=num=> parseInt([...num.toString()].map(n=> n*n).join(''))

I’d still classify this as “alarmingly readable”, but you can tell that if you kept chaining, you’d get there before too long. (Note that if you are working in a codebase that predates ES6, chaining will be much more effective in making your code confusing. Take advantage of this!)

Python, however, does not disappoint us:

sq=lambda n:int(''.join(map(lambda x:str(int(x)**2), list(str(n)))))

Now that is impressively illegible.

In its loathsome quest to force developers to adhere to its draconian vision of readability, Python appears to have left itself wide open to exploit what its creators no doubt intended to be a strength. Ignore the obvious intent behind the language design and call one method after another until even you can’t understand what you’ve done.

That’s it for part one.

I hope, as always, that this was helpful and informative. Obviously some of you probably didn’t know about the importance of writing really, really bad code, but with any luck this will help inform and correct coders who have been naïvely spending their time trying to make their code readable, modular, robust and flexible.

If you’ve read this in its entirety, you’re well on your way to turning your Python codebase into a snaking, writhing mess, your Ruby project into anything but a gem, your JavaScript backend into something that resembles a four-hundred-page manuscript covered in coffee that no one wanted to read in the first place, and making your C++ resemble a D-. (Actually, if all you did was lazily skim through this, you’re probably already at least halfway there.)

In Part II we’ll cover the evils of testing frameworks (and how to avoid and fool them), the beauty of side-effects, when and where to write comments (never), being clever, and further ways to obfuscate your code and f̶r̶u̶s̶t̶r̶a̶t̶e̶- delight your coworkers.

More where this came from

This story is published in Noteworthy, where thousands come every day to learn about the people & ideas shaping the products we love.

Follow our publication to see more product & design stories featured by the Journal team.

--

--