Flat Code: The Art of Writing Code That’s Easy To Read, Understand, and Maintain

Flat Code: The Art of Writing Code That’s Easy To Read, Understand, and Maintain

·

4 min read

Most discussions on code style are a waste of time.

There's rarely a compelling reason behind style suggestions other than personal preference.

Tabs or spaces?

2 spaces or 4?

Semicolons anyone?

In most cases, worrying about code style isn't valuable. What's more important is consistency. It's jarring to see inconsistent styles throughout the same project or worse throughout a single function.

Just pick something and stick to it.

That said, there is one area where I think it matters.

The code you write has to be readable.

It's important to make sure the code you write is readable. It's not enough that your code is easy to understand today. You want to make sure you'll be able to understand the code when you revisit it in a week or a month. Even more important is to make sure it's easy to understand for the other people on your team.

We've all seen those one-liners of code that seem to hold an application together by pure magic.

A monstrosity of code that gets stuffed into a single line in an effort to write the most compact solution.

Less code is always better, right?

Wrong!

You shouldn't have to exert that much mental effort to understand a single line of code.

(Double points if you check the git blame and you're the one who wrote the code 💀)

Deeply nested code is harder to read.

Unfortunately, it's common for codebases to suffer from this problem.

It's easier to read flat code than read nested code. Nested code forces you to keep track of the context around the nesting. The increase in mental overload makes the code harder to reason about. Your goal should be to reduce the time it takes to understand unfamiliar code.

Great!

So how do you do that?

Keep your code to the left of the file.

It won't always be possible, but it's a good goal to strive towards. If I notice this in my own code then I start to look for opportunities to refactor. The only time I avoid doing this is if the change results in code that's less readable, but that's usually not a problem.

So what're some common problems to watch out for?

Here are a couple of examples.

1. Unnecessary Conditionals

A common pattern I've seen is to return true and false explicitly.

While this isn't wrong, it's unnecessary. Instead of returning the booleans directly, it would be cleaner to return the condition itself. There's less nesting in the second example below which makes the code seem less complex.

// BAD
function checkSomething(something) {
    if (!!something) {
        return true;
    } else {
        return false;
    }
}

// GOOD
function checkSomething(something) {
    return !!something;
}

2. Guard Clauses: Code For The Happy Path

We've all come across deeply nested conditionals that represent the happy path of a function.

In this case, there's not much happening, but the relevant code is shifted to the right which makes it harder to read. While this example is a bit contrived, I think it illustrates the problem well enough. Instead of nesting the code this way, I find that it's easier to rule out the non-happy paths first with guard clauses.

This leaves the code after the guard clauses in a state that's easier to understand and read.

// BAD
function withoutGuards(something) {
    if (something) {
        if (something.things) {
            return something.things.map(someMapper);
        }
    }

    return [];
}

// GOOD
function withGuards(something) {
    if (!something) return [];
    if (!something.things) return [];
    return something.things.map(someMapper);
}

And yes! Before you say anything, I know since this is JavaScript code, we can solve this with optional chaining and nullish coalescing:

function withBetterTools(something) {
    return something?.things?.map(someMapper) ?? [];
}

Conclusion

When you're writing code it's important to communicate intention through your code.

One way to do that is to make sure the code you write is readable. Pushing your code to the left so that it's not as deeply nested is a great starting point. It makes it easier for you and other devs to understand the code over extended periods of time.