July 13, 2021

7 Rules Of Writing Good Code

7 Rules Of Writing Good Code

Good code — it’s a debatable mystery that plagues developers from all programming languages. It is often highly opinionated, vague, and at times conflicting.

We all know we should write good code, but what exactly does it mean? Some say to write as little code as possible. Others say to write ‘clean code’, whatever that is supposed to be.

Here are my 7 Rules of writing good code and the reasons why I advocate for them.

Rule #1: Don’t Write a Mystery Novel

Complexity is the bane of inherited code. It’s not cool. It’s not sexy. It’s also certainly not a sign of quality. The ability to simplify the complicated is the true indication of a good developer. Code is a language and the purpose of a language is to communicate with each other meaningful information.

Code becomes a mystery novel when you start to spend more of your time trying to figure out what trabool stands for and how it is relevant to the function you are trying to debug.

Call your namespaces, classes, functions, and variables by what it is. Don’t hide it in acronyms. trabool is a real thing. Apparently, it stands for ‘travel agent booking login’.

How did I find this out? About a week later and only by a watercooler chance chat with the business analyst.

Rule #2: Practicality over Purity

Programming is full of best practices. They’re there for a reason — to keep your code organized, standardized, and generally consistent. However, if you have to bend over backward to adhere to these ‘rules’, then it might not be the way to go.

Why?

Because rules often involve multiple levels of abstraction. When you’re trying to follow the ‘best practices’ to the letter, it might be a bit of an overkill for the size and context of your project.

Take Java as an example. It wants to fit everything in the object-oriented paradigm, resulting in a ton of boilerplate code. If you abstracted out every little thing, you’re going to write more code than necessary.

Writing practical code is finding the balance between following the rules and breaking the rules.

Rule #3: Errors Are Never In Silence

Errors are a developer’s debugging lifeline. Don’t let them pass silently. Make them loud. Log them out. Catch them before everything else runs.

For example, look at the code below.

'use strict';
...
const fakeAPI = function(callback){
    request(`https://someapi.com/api`, 
            {json: true}, 
            (err, res, body) => {
          if(res){
                    //do something
                }else{
                    callback(err)
                }
    })
}

On the surface, it looks innocent enough. However, you’re assuming that your API will work all the time. It might have thrown an error and given back a res and we would never know about the err. It’s a common StackOverflow copy-paste template.

Here’s a different way to look at the same code:

'use strict';
...
const fakeAPI = function(callback){
    request(`https://somesite.com/api`, 
            {json: true}, 
            (err, res, body) => {
          if(err) return callback(err)
          //rest of your code to deal with res
    })
}

You’re always assuming the worst — so you put your errors in first so it never fails in silence.

Rule #4: Understand Your Code. Never Guess.

You know that joke about copy-pasting from Stack Overflow? That’s not coding. That’s just you hoping that whatever someone else has written somehow magically works for your context.

What you might end up with is a lot of junk code that can create errors and conflicts further down the line. This is different from getting the syntax right. If you’ve got the general gist of an idea, sometimes a little training wheel is good for productivity.

If you don’t understand your code, then it means you don’t understand a particular concept. It’s endemic among new developers and we shouldn’t chuckle at the fact that copy-pasting from Stack Overflow is a thing. We should pause our coding and get back to learning how to code in that particular language properly.

Rule #5: Easy To Explain

Code is a communication tool. It doesn’t matter what level of seniority you are on the pay scale, if a junior who is proficient in the language cannot comprehend it, then it is not good code.

A program needs to be understandable by both the creator and the maintainer — three months after its creation when everyone has forgotten what it was all about and no one is emotionally attached to the project.

Self-explanatory code is code that spells out exactly why it exists, what it is doing, and why it needs to do what it does. In short, it is code that just gets to the point.

Don’t try to abstract unnecessarily for maximum performance. If it’s hard to understand, it’s hard to debug.

Rule #6: Flat Is ALWAYS Better Than Nested

No one wants to deal with nesting, no matter how clever you think they are at the time.

The more layers you need to dig into the code, the harder it becomes to hold the logic in your head. Then you have to deal with namespaces and scopes. If you’re working with JavaScript, too much nesting becomes an imploding nightmare. It pushes your code off to the side, runs on multiple brace pairs that you have to trace, and the task of trying to figure out when a certain scope is created and destroyed.

Nesting is also the thing that invokes the wtf deadpan mildly annoyed 😐emoji in Slack channel chats. Flat is always better than nested. If you need to nest something, there is probably an actual technique on how to achieve the thing you want to achieve — like recursions.

Rule #7: Special Cases Aren’t That Special

There is a good reason why design patterns exist — it is the culmination of solutions to commonly faced issues. We often convince ourselves that what we’re coding is an edge case — that it is special and should be an exception.

It might be. It might not. Chances are, it’s probably not.

Design patterns reduce coupling and create highly cohesive modules based on tested designs. It’s repeatable and it speeds up code comprehension. There is standardization and a general understanding of where things are. A developer inheriting your code spends less time decrypting what you’re trying to achieve.

Final Thoughts

The quality of your code is often judged by the person inheriting it. If they hate trying to figure things out, then it is a symptom of bad code. If debugging an error or issue is an easily traceable exercise, then it is a sign of good code.

Contrary to popular belief, bugs are not always symptomatic of bad code. Sometimes there’s a flaw in the logic, or something got missed. Sometimes libraries, frameworks, and module updates happen, which can lead to breakages.

Bad code is when code fails to communicate its intention and purpose for existing. When no one knows what’s going on — that is bad code.

These 7 rules are merely a guideline and are based on my experiences of working in teams, inheriting code from others, and general ongoing self-education. You might have your own to add — but overall, I don’t think the rules are disputable.

Thank you for reading and I hope you’ve enjoyed the piece.