JavaScript Best Practices — Defensive Programming

John Au-Yeung - Jan 27 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

JavaScript is a very forgiving language. It’s easy to write code that runs even if it has issues in it.

In this article, we’ll look at how to program defensively so that errors are handled gracefully.

Protecting Our Program from Invalid Inputs

We got to check our program for invalid inputs so that they won’t propagate in our systems.

For instance, we got to check for things like null , undefined , or invalid values all overt our code.

Also, we got to be aware of common security issues like SQL injection or cross-site scripting.

To do that, we do the following things in our program.

Check the Values of All Data from External Sources

We just can’t trust users to always enter valid data and that attackers won’t use exploits in our code.

Therefore, we should check everything thoroughly so that we can make sure that no invalid data gets propagated anywhere.

For instance, we should check for number ranges if code need numbers in a certain range.

Also, we shouldn’t use things like the eval function or the Function constructor which runs code from a string.

Check the Values of All Function Input Parameters

We got to check the values before we use them in a function.

For instance, we can write a function that dispenses beer to adults only as follows:

const dispenseBeer = (age) => {
  if (age < 21) {
    return 'you are too young';
  }
  //...
}
Enter fullscreen mode Exit fullscreen mode

We check age before dispensing beer so that we won’t mistakenly dispense beer to underage people.

Decide How to Handle Bad Inputs

Bad inputs have to be handled in a graceful way.

We got to check them and then find a good way to deal with them.

Returning a message or throwing exceptions are good ways to handle invalid inputs.

For instance, our dispenseBeer function can be changed to show an alert box instead:

const dispenseBeer = (age) => {
  if (age < 21) {
    alert('you are too young');
    return;
  }
  //...
}
Enter fullscreen mode Exit fullscreen mode

Or we can write the following:

const dispenseBeer = (age) => {
  if (age < 21) {
    throw new Error('you are too young');
  }
  //...
}
Enter fullscreen mode Exit fullscreen mode

We don’t need the return since throwing exceptions would end the execution of the function.

Assertions

Also, we can use assertions to check for inputs before we run the function.

For instance, we can write the following using the assert module as follows:

const assert = require('assert');

const dispenseBeer = (age) => {
  try {
    assert.equal(age >= 21, true);
    //...
  }
  catch {
    return 'you are too young';
  }
}
Enter fullscreen mode Exit fullscreen mode

This does the same thing as throwing an error since it checks if age is bigger than 21.

Otherwise, an assertion error would be thrown and ‘you are too young’ is returned.

This is the same as throwing an exception.

There’re many other kinds of assertions we can make with the assert module.

They include:

  • checking for equality
  • checking for inequality
  • checking for exceptions thrown
  • checking for deep equality
  • checking for deep inequality
  • …and others

Guidelines for Using Assertions

There’re some guidelines for using assertions.

Use Error-Handling Code for Conditions that are Expect to Occur

Error handling code should be used for checking for conditions that might be invalid.

In this case, we should handle those cases instead of using assertions.

Things like bad input values should be checked with error handling code.

Use Assertions for Conditions that Should Never Occur

If something should never occur, then we should use assertions to stop the code from running if they’re encountered.

We can run the rest of the code assuming that condition that we asserted to never occur won’t occur.

Avoid Putting Executable Code into Assertions

We should only use assertions to only check for things.

It keeps them clean and prevents any confusion.

Use Assertions to Document and Verify Preconditions and Postconditions

Preconditions that should be verified should have an assertion.

Since it;’s in the code, then we know what the codes check for when we read the code before running the rest of the code.

When the program ends, we can check for postconditions with assertions to check for conditions we look for.

Conclusion

We should check for invalid conditions before we run anything.

To do that, we can use assertions or error handling code. Assertions are better for checking for conditions that should never occur while error handling code are better for other cases.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .