DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 966,904 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for Improve code readability with conditional safeguards
Moses Karunia
Moses Karunia

Posted on

Improve code readability with conditional safeguards

"You shall not pass!" - Gandalf the Grey, 2001

TL;DR

  1. Define what your code supposed to do.
  2. Write safeguards. One safeguard should define one specific condition.
  3. Don't use else, if able.
  4. Don't nest if, if able.

Conditional is one of basic fundamentals in programming. And the most basic of many kind of conditionals in programming, is if statement.

Writing an if statement is easy, but if we are not careful, it can easily leads to giant spaghetti code mess, hard to read and debug.

I think, there are (not limited to) two things can lead to messy codes:

  1. Overnesting if statements
  2. Unnecessary else statements

You might say, "Just write a comment to explain!"

Well, comments are great and can be helpful. But they should not be used to justify a messy code.

So this time, I'm going to share with you, about how I usually write my conditionals.

I don't know what the official name of it, LOL. But I think I first read it somewhere as SAFEGUARDING.


CASE STUDY

To do this, first, we need to determine what our function should do. To make it easier to explain, let's jump to code:

(Code is in Typescript)

Say we want to add a polymorph skill to our game. Therefore, we write this code:

export const polymorph = (caster: Person, target: Person) : Beast => {
  return new Beast(from: target);
}
Enter fullscreen mode Exit fullscreen mode

Plain and simple. Now our target is polymorph-ed if we call the function.

But wait, not everyone should be able to cast polymorph! It will be broken if a knight is able to cast it.

So, let's safeguard it.

export const polymorph = (caster: Person, target: Person) : Beast => {
  try {
    if(caster.class !== 'WIZARD') {
      throw new Error('Only wizard can cast polymorph!');
    }

    return new Beast(from: target);
  } catch (error) {
      throw error;
  }
}
Enter fullscreen mode Exit fullscreen mode

There you go! Now, only wizards can cast polymorph.

Hmm.

Btw, aren't wizards need 100 mana to cast polymorph? Well, let's add the mana cost to the logic.

export const polymorph = (caster: Person, target: Person) : Beast => {
  try {
    if(caster.class !== 'WIZARD' && caster.mana < 100) {

      if(caster.mana < 100) {
        throw new Error('Not enough mana!');
      }

      throw new Error('Only wizard can cast polymorph!');
    }

    return new Beast(from: target);
  } catch (error) {
      throw error;
  }
}
Enter fullscreen mode Exit fullscreen mode

Umm. Nested if statement. Can we un-nest it?

export const polymorph = (caster: Person, target: Person) : Beast => {
  try {
    if(caster.class !== 'WIZARD') {
      throw new Error('Only wizard can cast polymorph!');
    }

    if(caster.mana < 100) {
      throw new Error('Not enough mana!');
    }

    return new Beast(from: target);
  } catch (error) {
      throw error;
  }
}
Enter fullscreen mode Exit fullscreen mode

Ah nice.

I think it's better. Improved readability by separating each safeguard's concern.

Wait. How if our target is currently immune?

Alright, let's add the logic to our function.

export const polymorph = (caster: Person, target: Person) : Beast => {
  try {
    if(caster.class !== 'WIZARD') {
      throw new Error('Only wizard can cast polymorph!');
    }

    if(caster.mana < 100) {
      throw new Error('Not enough mana!');
    }

    if(target.isImmune) {
      throw new Error('Target is immune!');
    }

    return new Beast(from: target);
  } catch (error) {
      throw error;
  }
}
Enter fullscreen mode Exit fullscreen mode

There you go, now polymorph can only cast by wizards, requires 100 mana, and cannot pierce immune targets.

And now, both of our players as well as the code maintainers are happy.


SUMMARY

  1. Define what your code supposed to do. (It'll be better if we separate the concern of each functions)
  2. Write safeguards. One safeguard should define one specific condition.
  3. Don't use else, if able.
  4. Don't nest if, if able.

CLOSING

What I really like about safeguard, is because it improve code readability and easier to define structured error messages.

It's like saying: "Ok, this is what the code should do. But, look at the safeguards! They prevents the code to function under wrong circumstances."

Oh, before anyone fire out their gun, I'm not against else statements. It's necessary sometimes. After all, it exists for a reason. But in some cases where we can omit it and improve code readability, why even bother to write it?

Less is more


NOTE

For you who don't know, throw-ing an error means stopping the function from continuing. It's similar like writing return;.

Top comments (0)

🌚 Life is too short to browse without dark mode