Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — Tools and Tests

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at some tooling and testing best practices for writing robust JavaScript code.

Use TypeScript

TypeScript is a language that’s an extension of JavaScript to provide flexible and optional type checking for JavaScript code.

It builds code into JavaScript so that it can be run on the browser or in the Node.js environment.

The code is checked for type errors before the code is built into the final artifact. This means that data type errors, which would be runtime errors if it weren’t checked by TypeScript are prevented.

This is a big class of errors that are eliminated. The type checking isn’t rigid. It provides many ways to annotate the types of objects and return types of functions.

For instance, we can create interfaces to annotate the types of objects as follows:

interface Pet {
    name: string;
    walk(): void;
}

In the code above, we created an interface to indicate that whatever has the type Pet must have the name string property and a walk method that returns nothing (hence the void return type).

Then we can use that as follows:

const dog: Pet = {
    name: 'joe',
    walk() {
        console.log('walk');
    }
}

In the code above, we have the name string property and the walk method in the dog object, which meets the requirement of the interface.

Therefore, the code will build and run by the TypeScript compiler. We’ll get an error if the types of anything are different from the interface.

Another good feature of TypeScript is the flexibility of the type checking. The types don’t have to be fixed, they can be flexible and dynamic as well.

For instance, we can create nullable types as follows:

interface Pet {
    name: string;
    walk?(): void;
}

In the code above, the walk method has been made optional with the ? after the method name. Now we can omit it in any object that has type Pet .

We can also create union types of multiple types as follows:

interface Pet {
    name: string;
    walk(): void;
}

interface Animal {
    breed: string;
}

const dog: Pet | Animal = {
    name: 'joe',
    breed: 'labrador',
    walk(){}
}

In the code above, we have the Pet | Animal type, which consists of the members of the Pet and Animal types combined together.

Therefore, our dog object can have any member from each interface included in it.

Also, we don’t have to specify the types of an object directly. If we already have an object in our code and we want another object to have the same data type as that one, we can use the typeof operator as follows to specify the type:

const cat = {
    name: 'james'
}

const dog: typeof cat = {
    name: 'joe'
}

In the code above, we specified the data type of the dog object to be the same as the data type for cat with the typeof dog expression. This way, we don’t have to specify any types directly.

TypeScript has many more features that can help us write more solid JavaScript code by preventing data type errors that are otherwise caught at runtime.

This is especially important with big apps where there’s more chance of these errors happening.

Therefore, we should consider using TypeScript for writing more robust JavaScript code.

Photo by Jerry Wang on Unsplash

Write Tests

We should write automated tests to check for regressions in our code so that we can have peace of mind when changing code and all the automated tests pass.

It’s easy to write automated tests with the Jest test runner, which has many features to let us write and run tests for both front and back end JavaScript apps.

For instance, we can write a simple function with unit test as follows:

math.js :

const multiply = (a, b) => {
  return a * b;
}
module.exports = {
  multiply
};

math.test.js :

const math = require('./math');

test('multiplies 1 * 2 to equal 2', () => {
  expect(math.multiply(1, 2)).toBe(2);
});

In the code above, the math.js module has the function multiply that we want to test.

Then in math.test.js , we added our test for testing the multiply function exported by math.js .

The file names are the convention for Jest tests, where we have the name of the production code file corresponds with the name of the test file.

Then when we run Jest, this test will run and should pass because 1 times 2 is 2.

If we write unit tests for every part of our app then it’s easy to know whether our app is still working properly.

Conclusion

TypeScript is probably the best type checker for JavaScript. It’s a natural extension of JavaScript to add type annotations and checking to our JavaScript code.

Writing automated tests is always a good idea so that we can check that our code is working properly after we make changes to it.

Categories
JavaScript Best Practices

JavaScript Best Practices — Code That We Shouldn’t Have

JavaScript is a very forgiving language. It’s easy to write code that runs but has mistakes in it.

In this article, we look at some code that we shouldn’t add to our JavaScript codebase.

No Unnecessary Labels

JavaScript has labels that we can label parts of our code. For instance, if we have 2 loops, we can label them as follows:

A: while (a) {
  break A;
}

B: while (b) {
  break B;
}

In the code above, we have label A to label the first while loop and B to label the second while loop.

However, the example above is useless because the label is just referenced within itself. So the code above is the same as:

while (a) {
  break;
}

while (b) {
  break;
}

Therefore, the labels are useless.

We should use labels to only reference other pieces of code which the label is used to label. For instance, we can write the following code:

A: while (a) {
  break B;
}

B: while (b) {
  break A;
}

No Case Statement Fallthrough

In switch blocks, we can write case statements or blocks without the break keyword at the end of each case .

This isn’t good because all the case statements or blocks will run even if a matching case is found within the switch statement.

For instance, if we have:

let foo = 1;
let bar;
switch (foo) {
  case 1:
    bar = 'x';
  case 2:
    bar = 'y';
}

Then we’ll see that bar is 'y' even though foo is 1, which is probably not what we want.

The correct way to write the switch statement is to write the following:

let foo = 1;
let bar;
switch (foo) {
  case 1:
    bar = 'x';
    break;
  case 2:
    bar = 'y';
    break;
}

Then bar will be 'x' as we expected. Alternatively, we can also write return instead of break as follows:

let foo = 1;
const baz = (foo) => {
  switch (foo) {
    case 1:
      return 'x';
    case 2:
      return 'y';
  }
}

let bar = baz(foo);

In the code above, we have return statements instead of break, and we placed the switch statement in the function so we can get the return value when calling it.

It’ll give us the same result as using break since it ends the code execution of the function.

No Floating Decimals

Floating decimals can be before or after a number in JavaScript. For instance, we can write the following:

let num1 = .1;
let num2 = 3.;
let num3 = -.5;

In the code above, we defined numbers with decimal points before or after a number.

This isn’t good because it’s not clear that the dot is for the dot operator for accessing properties or whether it’s a decimal point.

Instead, we should just put in the leading and trailing zeroes as follows:

let num1 = 0.1;
let num2 = 3.0;
let num3 = -0.5;

Photo by Vladislav Klapin on Unsplash

Assignment to Native Objects or Read-Only Global Variables

Read-only global variables like window or Object are read-only. Therefore, we shouldn’t be assigning new values to it.

For instance, we don’t want to do things like:

window = {};

since we don’t want window to be an empty object. Other things we don’t want include code like:

Object = null;
undefined = 1;
null = 2;

JavaScript strict mode should prevent these assignments from happening. If we didn’t use strict mode, then we should make sure that we don’t do things like this.

Modules are automatically set to use strict mode, so we don’t have to worry about this.

No Type Conversion with Shorter Notations

We can use the unary + , - , or ~ to convert values to numbers or boolean.

They aren’t very clear to some people, so we may want to limit their use. Therefore, instead of writing:

let b = !!foo;
let c = +'2';

We should convert them explicitly with built-in JavaScript functions as follows:

let b = Boolean(foo);
let c = Number('2');

Conclusion

We shouldn’t write code that is useless or don’t do what we expect them to. For instance, case statements and blocks in switch statements should always have break or return added.

Decimals should either not be used or we add leading and trailing zeroes to make it clear.

Also, we should never assign native global objects with our own values.

Finally, we may want to convert data types explicitly with our own code to make our type conversion code more clear.

Categories
JavaScript Best Practices

JavaScript Best Practices — More Code That Shouldn’t be Added

JavaScript is a very forgiving language. It’s easy to write code that runs but has mistakes in it.

In this article, we’ll look at more code that shouldn’t be added to our JavaScript code.

No Implicit Global Variable Declaration

A global variable declaration can be done in various ways in JavaScript. Without strict mode enabled, we can declare a global variable with var .

For instance, we can write something like the following to create global variables without strict mode:

var x = 1;

Since the code above is in the top level, if we have strict mode off, we’ll declare a variable in the global scope.

In the browser, the code above is the same as window.x = 1 .

Therefore, using var isn’t the best way to declare a variable at the top level without strict mode.

Now that we have let and const , we should use those instead since they won’t declare a global scope, and also, their scope is consistent anywhere. They always declare variables in a block scope.

Even in the top-level, they won’t declare global variables. Preventing declaring global variables accidentally can also be done with enabling strict mode.

This way, the example we have above won’t attach x as a property of window .

No Implied eval() Code

setTimeout and setInterval can accept a string that runs the code as a their callback.

For instance, the following:

setTimeout("console.log('foo');", 100);

does the same thing as:

setTimeout(() => console.log('foo'), 100);

setInterval has the same feature, so:

setInterval("console.log('foo');", 1000);

does the same thing as:

setInterval(() => console.log('foo'), 1000);

This means that we can insert any string into either function and run the code.

The string is interpreted in the global scope, so anybody can set variables inside themselves if they wish and run arbitrary code. Running code from a string is also slower. Therefore, we should avoid passing strings into setInterval and setTimeout .

Instead, we should pass in a callback.

No this Keywords Outside of Classes or Class-Like Objects

The keyword this shouldn’t be used where it isn’t needed. Therefore, it shouldn’t be used outside of constructor functions, object literals or classes.

Object literals can have this inside its methods. Getters and setters inside objects, constructor functions, and classes can also reference this since they’re part of an instance.

this can also be referenced in callbacks and traditional functions that calls call , apply , or bind .

For instance, we can write the following code:

function() {
  this.foo = 0;
}

The following is also good:

class Foo {
  constructor(foo) {
    this.foo = foo;
  }
}

Things like arrow functions should have this in it, since it doesn’t bind to this , so:

() => this.foo;

isn’t proper JavaScript code.

We can also use call as follows:

(function foo() {
  console.log(this.foo)
}).call({
  foo: 'bar'
})

Then we see that the console log outputs 'bar' , which is the valur of foo that we pass into call .

Photo by Kevin Mueller on Unsplash

Disallow Iterator

The __iterator__ property was a custom property of an object’s prototype that’s used to create a custom iterator for for...in loops.

Now that JavaScript has iterators, we don’t need this. For instance, the following is now obsolete code:

Bar.prototype.__iterator__ = function() {
    return new BarIterator(this);
}

We shouldn’t write code like that anymore now.

Disallow Labeled Statements

We can add labels to JavaScript in conjunction with break and continue to control the flow of multiple loops.

For instance, if we have:

outer:
  for (let i = 0; i < 5; i++) {
    while (true) {
      break outer;
    }
  }

Then we end the for loop when the while loop is first executed. The better way to use break and continue is without labels.

The code above is more error-prone and harder to understand than the code without labels.

For instance, we should write:

while (true) {
  break;
}

so we don’t have labels.

Conclusion

A global variable declaration shouldn’t be added accidentally to our JavaScript code. To do this, we should make sure strict mode is on and we aren’t using var if it’s off.

We can pass in a string to setTimeout and setInterval to run code. They’re all run in the global scope. This lets anyone sets global variables and run code as they wish. Also, it’s slower than running the actual code. Therefore, we shouldn’t use strings as callbacks in those functions.

this should only be included when it’s needed. They’re needed only in constructor functions, classes, and object methods.

The __iterator__ is obsolete and shouldn’t be used. Finally, labels are also not commonly used for marking loops for reference and shouldn’t bee used since it’s error-prone and hard to read.

Categories
JavaScript Best Practices

JavaScript Best Practices — Error Processing and Exceptions

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

In this article, we’ll look at how to handle errors in JavaScript programs.

Design Implications of Error Processing

If we handle errors in our system, then we got to test our error processing code.

This is to ensure that we did everything properly.

If we have return values, then we test the return values.

We should never ignore error information.

If we have a consistent approach for handling errors, then we should follow it so that we can have issues with inconsistency and duplication of error processing code.

Exceptions

Exceptions are the means of which code can pass along errors from the code that caused the error to the surface.

If we don’t want our program to crash, then we should catch them so that we can deal with them gracefully.

In JavaScript, we can throw errors by using the throw keyword on the Error class or subclass of Error .

To catch errors, we can use try...catch to catch them.

If we want to run code regardless of whether an exception is thrown, we can put them in the finally clause.

For instance, we can write the following to throw an error:

throw new Error('error');

To catch exceptions that are raised from code that may cause errors, we write:

try {  
  errorThrowingCode();  
  //...  
} catch (ex) {  
  // handdle errors  
}

Where errorThrowingCode may throw errors.

A finally clause may be added after the catch block.

Use Exceptions to Notify Other Parts of the Program About Errors that Should not Be Ignored

The whole point of throwing exceptions is that we can’t ignore them without users seeing them.

Therefore, we should use exceptions to notify other parts of our programs about errors that shouldn’t be ignored.

Throw an Exception Only for Conditions that are Truly Exceptional

Even though we should throw exceptions for things that shouldn’t be ignored, we shouldn’t use them everywhere.

If we have too many then, then we’ve to have code everywhere to handle exceptions.

We don’t want that, so we should think about using exceptions for those errors that must be handled for proper operations of our program.

Exception handling makes our code more complex as we have to catch them and then handle them.

There’re better ways to deal with non-critical errors like checking for values and do something instead of throwing exceptions.

Don’t Use an Exception to Pass the Buck

If errors can be handled locally, then we should do that.

Handling them elsewhere just makes our code more complex.

Include in the Exception Message All Information that Led to the Exception

We should include useful information in our exceptions so that we can have enough information to debug and fix the issue that caused the exception to be raised.

It just makes dealing with errors easier if we give everyone more information so that we can deal with them.

Avoid Empty Catch Blocks

Empty catch blocks are useless, so we shouldn’t include them.

We got to handle exceptions that are thrown rather than doing nothing when an exception is caught.

If we want to do nothing, we should at least log about the error.

Know the Exceptions Our Library Code Throws

Knowing exceptions that our libraries throw is important so that we can handle them when they arise.

Failing to catch them will be caught our program to crash and that won’t be good user experience.

Consider Building a Centralized Exception Reporter

Creating a centralized exception reporter makes logging and formatting exceptions a breeze as all that is done in one place.

It’s better than handling them all separately in different places.

Standardize Our Project’s Use of Exceptions

For the sake of keeping things consistent, we should standardize how exceptions are handled in our code to reduce developers’ cognitive load.

We can create project-specific exception classes and define code that is allowed to throw exceptions.

Also, we can centralize reporting and logging.

Conclusion

There is a lot to think about when we throw exceptions.

We got to think about when and how to throw exceptions. Where they handle and whether we can create our own error classes.

Standardization would make our work easier.

Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — Clean Code

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at some ways to keep our JavaScript code clean and easy to read so that changing it is a piece of cake.

Keep Code Organized

Our JavaScript code should be organized well so that they can be easily reasoned with. Well organized code don’t repeat anything.

Functions and classes inside all do one thing only and no more. They’re mostly independent of each other, and they only expose what’s needed to the outside world, which is the bare minimum to avoid tight coupling.

This is easy to do if we organize things cleanly from the beginning. For instance, we can write the following code to keep things clean:

fruit.js

export class Fruit {
  constructor(name) {
    this.name = name;
  }
}

person.js

export class Person {
  constructor(name) {
    this.name = name;
  }
}

index.js

import { Fruit } from "./fruit";
import { Person } from "./person";
const sentence = `${new Person("foo").name} likes ${new Fruit("apple").name}`;
console.log(sentence);

In the code above, we have 3 modules. The first 2, person.js and fruit.js all have one class.

They all have one class each and the class only represent one thing. The Person class represents a person and the Fruit class represents a fruit.

Then in index.js , we import both of them and reference them in the string.

With the way we organized the code, the classes don’t reference each other. Each class only does one thing, and we only reference the classes when it’s necessary.

Don’t Write Clever Code

Clever code is bad because they’re usually hard to read, even though they’re usually shorter.

Simple solutions are better for maintenance than clever code. Clever but hard to read code isn’t good since they’re hard to reason with.

For instance, the following code is an example of clever but hard to read code:

const foo = bar ? bar ? qux ? 0 : 1 : 2 : 3;

In the code above, we have a series of nested ternary operators used in one line, which is shorter than writing out each if statement directly, but it’s hard to read.

Many people probably don’t understand what it’s doing. They have to write out the parentheses in their head so that they can where each ternary operation starts and ends.

Therefore, we shouldn’t have nested ternary operators used like that. Instead, we should write use if statements or only use ternary operators to return one thing or the other for one condition.

Debugging is harder than programming, and we often have to revisit old code to debug and make changes. So we shouldn’t make our job even harder by writing clever code that is hard to read.

Photo by Charis Gegelman on Unsplash

Splitting Code in Ways That They Can Be Tested Easily

Our code should be organized in a way that can be tested easily. This means that they should be exposed to the outside so that they can tested with unit tests.

We should also aim to write pure functions. Pure functions always return the same output for a given set of inputs. This means that they can easily be tested.

Side effects should be minimized in functions so that we don’t have to check code that’s outside the function for correctness when we’re testing.

For instance, we can write our code and tests as follows:

math.js

const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
module.exports = { add, subtract };

math.test.js

const { add, subtract } = require('./math');

test('adds 1 + 2 to equal 3', () => {
  expect(add(1, 2)).toBe(3);
});

test('adds 1 - 2 to equal -1', () => {
  expect(subtract(1, 2)).toBe(-1);
});

In the code above, we have math.js with the functions that we want to test. They’re both pure functions so that they always return the same output for the given set of inputs.

Then in math.test.js , we have 2 tests that are run with Jest to test those 2 functions that we imported from our math.js module.

We called add and subtract to test them and check their values with Jests’s built-in expect and toBe methods.

We want to organize our code this way so we can easily expose them for testing.

Also, they have no side effects so that they can be tested on their own without checking results on code that’s outside.

Conclusion

Code organization is important for writing robust JavaScript since it’s harder to break clean code. They shouldn’t be tightly coupled. Also, side effects should be eliminated as much as possible for easy testing and reasoning.

Finally, clever code should be avoided since they’re often hard to read.