Categories
JavaScript Best Practices

JavaScript Clean Code — Law of Demeter

The Law of Demeter states that we should hide the implementation details of our code as much as we can. This ensures that we have loose coupling between our code.

In this article, we’ll look at how to apply the Law of Demeter to our code.

Why do we Need Loose Coupling?

Loose coupling means that we don’t have to worry about changing a lot of code when one piece of code changes.

Each unit only has limited knowledge about other units so that only a few parts of the other units are referenced.

They only talk to their immediate friends, so that they don’t talk to other parts that aren’t related.

So this means that if we have 2 classes A and B , then A only references the methods of B than has to be referenced. Other members are kept private along with other implementation details.

How do we apply the Law of Demeter?

We can apply the Law of Demeter to our JavaScript code by writing it in a way that references a few classes and members of them as possible.

An example of classes that references each other too much is the following:

class PostalCode {
  constructor(postalCode) {
    this.postalCode = postalCode;
  }

  setPostalCode(postalCode) {
    this.postalCode = postalCode;
  }
}

class Address {
  constructor(streetName) {
    this.streetName = streetName;
  }

  getPostalCode() {
    return this.postalCode;
  }

  setPostalCode(postalCode) {
    this.postalCode = new PostalCode(postalCode);
  }
}

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

  setAddress(address) {
    this.address = new Address(address);
  }

  getAddress() {
    return this.address;
  }
}

We have the Address class that references PostalCode, and Person class that references Address and Occupation .

If any of Address and PostalCode change, then we have to change the Person and Address class.

Also, we have to set the postal code of a Person by writing:

person.getAddress().getPostalCode().setPostalCode('12345');

That’s a lot of chaining involving returning instances of different classes. If any of these methods change, then the whole chain has to be rewritten.

What we should do instead is to combine all the references into one method as follows:

class PostalCode {
  constructor(postalCode) {
    this.postalCode = postalCode;
  }

  setPostalCode(postalCode) {
    this.postalCode = postalCode;
  }
}

class Address {
  constructor(streetName) {
    this.streetName = streetName;
  }

  getPostalCode() {
    return this.postalCode;
  }

  setPostalCode(postalCode) {
    this.postalCode = new PostalCode(postalCode);
  }
}

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

  setAddress(address) {
    this.address = new Address(address);
  }

  getAddress() {
    return this.address;
  }

  getPostalCode() {
    return this.postalCode;
  }

  setPostalCode(postalCode) {
    this.postalCode = new PostalCode(postalCode);
  }
}

Then we only have to update the Person class if the PostalCode class changes instead of updating the whole chain of calls just to update the getting and setting the postal code when the PostalCode class changes.

The point is that we should have to know the whole system to get something done.

PostalCode doesn’t have to be connected to Address since they can be changed individually.

If we couple them together, then we have to know about Address before changing PostalCode .

The example above shows coupling that can be avoided and it should be.

Facade Pattern

We can also use the facade pattern to hide the complexity of a system so that we don’t have to know about them.

For example, we can hide multiple classes behind a facade class and then use the facade class to indirectly interact with multiple classes that hide behind the facade class as follows:

class ClassA {

}

class ClassB {

}

class ClassC {

}

class Facade {
  constructor() {
    this.a = new ClassA();
    this.b = new ClassB();
    this.c = new ClassC();
  }
}

class Foo {
  constructor() {
    this.facade = new Facade();
  }

}

In the example above, the Foo class knows nothing about what’s behind the Facade class. The Facade class holds instances of ClassA , ClassB , and ClassC .

It provides a simple interface to a complex system which consists of ClassA , ClassB , and ClassC .

When any of the classes behind Facace changes, we just have to change the Facade class.

It serves as an entry point to all we want to do with those classes. Instead of referencing them all individually and creating a mess of references, we have one unified interface for all of them.

This satisfies the Law of Demeter because we only access the Facade class to do anything with ClassA , ClassB , and ClassC . We don’t have to know about the underlying implementation of them.

It makes the software easy to use, understand and test since we only have to use and test Facade to interact with all the classes underneath.

It removed the need to reference multiple parts of a complex system since the Facade class provides all we need.

If the code underneath the facade class is poorly designed, we can also wrap around it with a well-designed API that helps people using the facade class to work with it in an easy way.

Most importantly, tight coupling is eliminated since nothing but the facade class references the complex code underneath it.

Conclusion

The Law of Demeter is all about hiding implementation as much as possible to outside code so that they won’t have to reference different parts of the code to get something done.

We should only create classes that talk to closely related classes, rather than talking to everything.

Talking to everything creates a mess of references that’s hard to figure out when we need to change the code.

One good way to implement the laws in the Law of Demeter is to use the Facade pattern. The pattern states that we have a facade class that serves as the entry point for other classes which are part of a complex system.

It’s used to provide an easy to use API which hides the implementation underneath. So if we do need to change the code underneath, we only have to update the facade class rather than everything that references the implementation code underneath.

Categories
JavaScript Best Practices

JavaScript Clean Code: Error Handling

Error handling is an important part of programs. There are many situations where our programs encounter values that are unexpected and we have to properly handle them.

In this article, we’ll look at how to handle them so that errors are easily found easily and gracefully handled.


Exceptions are Better than Return Codes

Throwing exceptions is better because they let us know that an error exists and that we have to handle it.

Most modern programming languages have exceptions built-in, so we should throw them instead of returning an error code.

Error codes aren’t as explicit and may be missed. Exceptions are also much cleaner since we don’t have to check all the codes that may be returned.

For example, if we return error codes in our functions then we may have code that looks something like this:

We have to return all the error codes in our setNumFruit method. Also, before we do something after the class definition, we have to check all the error codes.

We can throw exceptions instead:

We’ve eliminated the need to check all the error codes by wrapping the code we want to run in a try block. Now we can just catch the error instead of checking all the error codes that may be returned.

This is much better than checking all error codes before doing something — it’s especially important as code becomes more complex.


Write Try-Catch-Finally

We should wrap our try in the code that throws exceptions that we want to catch. It creates its own scope for block-scoped variables so anything declared with let or const can only be referenced in the try block.

Variables declared with var are hoisted so that they can be referenced outside the block. We won’t get an error even if they’re referenced outside the block. This will get us 1:

try {  
  var x = 1;  
} catch (ex) {  
  console.error(ex);  
}  
console.log(x);

But this will get us Uncaught ReferenceError: x is not defined:

try {  
  let x = 1;  
} catch (ex) {  
  console.error(ex);  
}  
console.log(x);

Don’t Ignore Caught Errors

We should report our errors when they’re caught. They shouldn’t be caught and then ignored. This is because we don’t want to sweep underlying issues under the rug.

Reporting exceptions let us know about the error and then handle it accordingly.

The examples above, like the console.error, call in the following:

try {  
  const error = fruitStand.setNumFruit(1);  
  console.log(fruitStand.numFruits);  
} catch (ex) {  
  console.error(ex);  
}

Thi s one of the ways to report the error. We can also use other libraries to report the error in a central place.


Don’t Ignore Rejected Promises

Like any other exception, rejected promises also need to be handled. They can be handled with the callback that we pass into the catch method or use the try...catch block for async functions — they’re the same.

For example, we can report the error by writing the following:

Promise.reject('fail')  
  .catch(err => {  
    console.error(err);  
  })

Or for async functions we can write this:

(async () => {  
  try {  
    await Promise.reject('fail')  
  } catch (err) {  
    console.error(err);  
  }  
})()

Providing Context with Exceptions

We should provide enough context in exceptions that other developers can pinpoint the error when an exception is thrown.

We can get a stack trace with exceptions in JavaScript, but it may not provide all the information we need.


Conclusion

When we handle errors, throwing exceptions is better than returning error codes since they let us use the try...catch block to handle errors. This is much cleaner than checking multiple error codes.

When we throw exceptions, we have to provide enough details to pinpoint the problem.

We shouldn’t just ignore errors after catching them. We should at least report them so that they can be looked at.

Finally, rejected promise errors should be handled the same way as other exceptions.

Categories
JavaScript Best Practices

JavaScript Clean Code — Objects and Classes

When writing clean code we have to be careful about the objects and classes or constructors that we define. We don’t want to expose data that we don’t want the outside world to view, and we want our data structures to be defined in a way that’s easy to reuse.

In this article, we’ll look at how to organize our data in a way that’s easy for us to use.

Use Classes Over Constructor Functions

We should use classes instead of constructor functions. It’s much clearer and inheritance can be done much more easily.

They’re the same underneath. Class syntax is syntactic sugar for the constructor function syntax.

For example, we can define a Person class as follows:

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

We can see that our constructor takes in name and age as parameters and set it as values of instance variables.

Where it shines is when we need to extend the class. We can create an Employee class that has the instance variables of the Person class and extends it as follows:

class Employee extends Person {
  constructor(name, age, employeeCode) {
    super(name, age);
    this.employeeCode = employeeCode;
  }
}

All we have to do is to call the superclass’ constructor with the super call, and then we can set the instance variables of the Employee class in the constructor.

Using Getters and Setters

Getters and setters are good for hiding the actual data that’s being accessed. We can have computed properties, which means that we can abstract way the implementation of those computed properties so that we don’t have to worry about changing the code that uses them when we change the implementation.

Also, setters let us validate before setting the data rather than setting them directly.

Likewise, error handling and logging can also be added easily into them.

We can also lazy load object properties with getters.

For example, we can use getters as follows:

class Rectangle {
  constructor(length, width) {
    this._length = length;
    this._width = width;
  }

  get area() {
    return this._length * this._width;
  }
}

As we can see, we can use it to create a computed property to get the area of a rectangle.

Then we can use it as follows:

let rectangle = new Rectangle(1, 2);
console.log(rectangle.area);

We can add validation into setters as follows:

class Rectangle {
  constructor(length, width) {
    this._length = length;
    this._width = width;
  }

  get area() {
    return this._length * this._width;
  }

  get length() {
    return this._length;
  }

  set length(length) {
    if (length <= 0) {
      throw new Error('Length must be bigger than 0');
    }
    this._length = length;
  }

  get width() {
    return this._width;
  }

  set width(width) {
    if (width <= 0) {
      throw new Error('Width must be bigger than 0');
    }
    this._width = width;
  }
}

Note that it’s also a good idea to define getters so that we can access the properties’ values.

Keeping Members Private

There’re no private variables in JavaScript classes, so we should define private variables in block scope so that they won’t be exposed to the public with let and const .

Chaining Methods

Chaining methods make calling a series of functions less verbose. We can define a function that can be chained by returning this .

For example, we can write the following:

class Person {
  setName(name) {
    this.name = name;
    return this;
  }

  setAge(age) {
    this.age = age;
    return this;
  }
}

Then we can use it as follows:

const person = new Person().setName('Joe').setAge(10);

Then when we log person , we get:

{name: "Joe", age: 10}

This is a common pattern that’s used in many libraries like jQuery and Lodash.

Preferring Composition Over Inheritance

We should leave a class inheritance for ‘is-a’ relationships only. An is-a relationship is a subset of some bigger entity. For example, an employee is a person.

Otherwise, we should use composition instead. For example, if we want to keep the address for a Person, we can create a class called Address and instantiate it in the methods of the Person class and use it there instead.

A Person has a ‘has-a’ relationship with Address , so we shouldn’t use inheritance in this case.

For example, we can write something like the following code:

class Address {
  constructor(streetName) {
    this.streetName = streetName;
  }
}

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

  setAddress() {
    const address = new Address('123 A St.');
  }
}

Conclusion

The class syntax is much easier to understand than the constructor function syntax, especially when we need to do inheritance. Therefore, it’s time to move away from the constructor function syntax as fast as we can.

We should only use class inheritance in ‘is-a’ relationships. Otherwise, we should use composition instead.

Also, we can use getters and setters to keep the implementation of the members underneath private. We can use getters for computed properties and setters for running code before setting values to a member. For example, we can run some validation code before setting the value with a setter.

Chaining methods is also a commonly accepted way to clean up code by making calls less verbose.

Categories
JavaScript Best Practices

JavaScript Clean Code — Vertical Formatting

Formatting code in an easy to read way is an important part of keeping code clean. Code that isn’t formatted properly takes more brainpower to interpret and understand by humans.

In this article, we’ll look at how to format JavaScript code consistently so that they can be read easily by looking at vertical formatting.

Why do we Need to Format Code?

Code formatting is important because is all about communication. Readability is important for ourselves and other readers.

A code that is a jumbled mess prevents people from understanding the code. Other people can’t work on it without understanding the code.

Also, people reading the code also have to follow through with the cleaning up the code. We don’t want them to suffer because of bad formatting that can easily be fixed.

Vertical Formatting

The number of lines of code is should be less than 500 lines. Smaller files are easier to understand than big files. Big files take longer to read and so more time is spent reading code and than doing the actual work.

Source code is like a newspaper article. The farther down we go, the more detailed the article gets. This is the same for code. We have an introduction with declarations of variables and functions and then as we get lower, we get more implementation details of the code.

Blank Lines

Blank lines are important between different entitles. They’re especially important between functions and class definitions. Without them, everything becomes hard to read and frustrates readers.

For example, the following is hard to read:

class Foo {metho1(){}metho2(){}}
class Bar {metho1(){}metho2(){}}

However, the following is much easier:

class Foo {
  method1() {}

  method2() {}
}

class Bar {
  method1() {}

  method2() {}
}

So we should put some blank lines in our code.

It’s hard to focus on a section of code when they’re all bunched together.

Vertical Density

As we can see above, the code shouldn’t be too dense vertically. Always leave some room between groups of code.

We can group variable declarations together without blank lines and classes in their own group as follows:

let x = 1;
let y = 2;

class Foo {
  method1() {}

  method2() {}
}

class Bar {
  method1() {}

  method2() {}
}

Vertical Distance

It’s frustrating to have to jump from one function to the next and scroll up and down the page all the time to read our code. It makes everyone confused.

Also, it’s frustrating to jump through tons of files to find the original definition of something.

To prevent this, related concepts should be kept close to each other.

Variable Declarations

Variable declarations should be as close to their usage as possible so that their usage can be found quickly without scrolling around or jumping through different files.

Loop control variables should be declared within the loop statement so that readers will know right away that it’s used in the loop.

Instance Variables

Instance variables should be declared on top of the class so that we know that they can be found right away.

We can put them in the constructor so that we know that they’re there for us to use or change.

For example, the following class:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  getPoint() {}

  setPoint() {}
}

Is clearer than the following:

class Point {
  getPoint() {}

  setPoint() {}

  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

Since most people expect the initialization code for a class to be at the top. The constructor has the initialization code, so we should put it on top.

Dependent Functions

Functions that depend on each should be close together so that we don’t have to scroll through a file to find the original definitions of the functions that are called.

This reduces frustration as we have everything close enough together to be on one screen.

For example, if we have a chain of calls like the following, we should keep them together:

class Foo {
  foo() {
    this.bar();
  }

  bar() {
    this.baz();
  }

  baz() {}
}

Related Code

Related code should be near each other so that we don’t have to scroll to find related concepts in our code. They may not have direct dependence, but they’re closely related enough that we need to find them together.

For example, the following class has methods that are closely related to each other:

class Assert {
  assertTrue() {}

  assertFalse() {}

  assertNotUndefined() {}
}

All 3 assert that some condition is met, so they’re related concepts, and so they should be close together.

Vertical Ordering

The function that’s called should be below a function that does the calling. This creates a nice flow from high to a low level.

The high-level concepts are important, so they should be on top, then we can look at the lower level helper functions.

So the example above also applies here:

class Foo {
  foo() {
    this.bar();
  }

  bar() {
    this.baz();
  }

  baz() {}
}

foo calls bar to do something and bar calls baz to do another thing. Together, foo is the highest level since it’s called first, and then the calls go down the chain to baz.

Conclusion

Code formatting is important. Without it, the code is a mess that’s hard to read and thus hard to maintain. We have to be proactive in cleaning up code with messy formatting.

To do this, we have to look at the vertical formatting of the code first. Each file should have thousands or more of lines. Also, blank lines are important between groups of code like functions and variable declarations.

Also, related concepts should be grouped close together to reduce the time to search for these entities together.

Instance variables should be on top so that they’re easy to find. Other variable declarations should also be close together, but they should be near where they’re going to be used to minimize scrolling.

Finally, high level code should be above lower level helper code.

Categories
JavaScript Best Practices

JavaScript Clean Code — Horizontal Formatting

Formatting code in an easy to read way is an important part of keeping code clean. Code that isn’t formatted properly takes more brainpower to interpret and understand by humans.

In this article, we’ll look at how to format JavaScript code consistently so that they can be read easily by looking at horizontal formatting.

Horizontal Formatting

With screens being bigger than the olden days, we can have horizontal lines that are longer than before.

80 characters long were the standard in the olden days, but now 100 to 120 is also fine.

The point is that most people shouldn’t have to scroll horizontally to read our code.

Horizontal Openness and Density

There should be some spaces between some entities in a horizontal line of code. Good places to put spaces are between variables and operators. Also, the space between literals and operators are also good.

We do not need a space between the method name and the opening parentheses. It doesn’t make as much difference as between operators and variables or literals.

For arrow functions, we should have a space between the closing parentheses, the fat arrow, and the opening brace.

For example, a class with clean horizontal formatting may look something like the following:

class Calculator {
  constructor(a, b) {
    this.a = a;
    this.b = b;
  }

  add() {
    return this.a + this.b;
  }

  subtract() {
    return this.a - this.b;
  }

  multiply() {
    return this.a * this.b;
  }

  divide() {
    return this.a / this.b;
  }
}

We have a space between the arithmetic operators and no space between the method name and the open parentheses of each method.

Each line is also less than 120 characters long.

Arrow functions may look something like the following:

const add = (a, b) => a + b;

We can also see that the parameters list also have space after the comma.

Horizontal Alignment

We don’t have to align variables declarations so that they’re horizontally aligned with each other.

For example, we don’t have to do the following:

let foo = 1;
let x   = 2;

We can keep it as:

let foo = 1;
let x = 2;

We can just let a code formatter do this kind of spacing change automatically.

Indentation

A code file is like an outline. We look at the high-level information on the outside and as we go deeper, we get to the nested code.

To make the hierarchy visible, we indent the blocks so that the hierarchy is visible to us.

We can do this by adding 2 spaces for indentation. However, we usually don’t have to do this automatically since code formatters will do it for us. We just have to remember to run it.

The indentation applies to blocks like conditionals and loops.

For example:

const loop = ()=>{if(true){for(let x of [1,2,3]){console.log(x)}}};

is much harder to read than:

const loop = () => {
  if (true) {
    for (let x of [1, 2, 3]) {
      console.log(x)
    }
  }
};

We can easily discern the if block and for in the second example, while the first example is almost completely unreadable. As we can see, spacing and indentation are quite important.

Breaking Indentation

For short functions, especially single line arrow functions, we can keep them in one line if they’re less than 120 characters long altogether.

However, for anything else, we should stick to the usual horizontal formatting rules.

Team Rules

If we work on a team, then it’s important to keep a set of rules for formatting code. Fortunately, we just have to run the code formatter of the team’s choice most of the time. This is at least true for horizontal formatting.

Vertical formatting rules like variable and function grouping has to be looked at in code reviews since they can’t be fixed automatically.

For horizontal formatting, we can use tools like ESLint, JSLine or Prettier to format our code.

We just run them automatically whenever we like to format the code.

Modern text editors like Visual Studio Code and Sublime also have add-ons to format code with them.

There’re various preset rules like the default rules that come with these linters and there are also alternatives like the Airbnb rules.

The team can agree on which one to pick and then add it to their code, then horizontal formatting will be done automatically.

Conclusion

There’re a few rules for horizontally formatting code. We should have proper indentation for blocks so developers can follow the code.

Spaces should be added between variables or literals and operators so that we can see the operations more easily.

Each line should be 120 characters or less so that we don’t have to scroll back and forth to read a line of code.

All these things can be done automatically by programs like ESLint, JSLint, and Prettier. They can be used with the default rules or configured with other rules like the Airbnb linting rules for ESLint.

Most modern code editors and IDEs like Visual Studio Code and WebStorm also have code formatters built-in or available as extensions.

They help keep a consistent clean style without developers doing extra work for horizontal code formatting.