Categories
JavaScript Best Practices

JavaScript Clean Code — Smells and Heuristics

ad code has lots of unique characteristics. In this article, we’ll look at each one and what they are. We look at writing comments, functions, and general code smells and heuristics.

Comments

Inappropriate Information

Information that shouldn’t be in the comments like author and changelogs are in the comments. They should be in source control systems, bug trackers, and other record-keeping systems.

Change histories should be in source control systems for example. It has metadata for authors, code changes, change date, etc. These shouldn’t be in the comments.

Comments should be for technical notes about the code.

Obsolete Comment

Comments that are old, irrelevant, or wrong are misleading. They get old quickly. The code should be clean enough to not need so many comments.

They become outdated quickly, so they should be avoided.

Redundant Comment

If the code adequately explains itself, then we don’t need comments explaining it. JSDoc that says nothing more than the signature isn’t also very useful.

They should say things that can’t be shown by the code.

Poorly Written Comments

Comments that are worth writing should be written well. We should make sure that they’re the best comments that we can write.

Commented-Out Code

Commented out code can be misleading. Why are they still there if they’re commented out?

We should delete the code if they aren’t needed. They can also be reverted from the source control system’s change record.

Environment

Build Requiring More Than One Step

Builds shouldn’t require more than one step. The more things we have to do manually, the worse that everyone suffers.

We shouldn’t have to do manual operations like checking code out from source control or run a bunch of commands and scripts every time we have to run a build.

There’re so many build pipeline solutions that the button should be a one-click process.

Tests Requiring More Than One Step

Running tests should also be easy. All tests should be run with one command. Either we can run commands with one click on an IDE or by typing in one command.

Functions

Too Many Arguments

Functions should have a few arguments as possible. No arguments are best. More than 3 is questionable.

Output Arguments

We shouldn’t be returning arguments straight at the end of the function as-is. It just makes no sense.

Flag Arguments

Flag arguments mean that a function does more than one thing, so they should be eliminated.

Dead Function

Functions that aren’t called should be removed. Dead code takes up space and misleads people. We can always get it back from source control history.

General

Multiple Languages in One Source File

One file should only be one language. The more language is in a file, the more confusing it is.

Clean separation of logic and markup is always good. JSX is just a different syntax for JavaScript, so it’s actually one language.

Obvious Behavior That’s Unimplemented

Obvious behavior should be implemented. If it’s so useless that it can stay unimplemented, then we can probably eliminate it.

We should implement obvious functionality as described by a function so that the name isn’t misleading.

Incorrect Behavior at the Boundaries

Developers often trust their intuition when they write their functions and think that everything works. We often ignore corner and boundary cases.

We should check our code by writing tests for these conditions and don’t assume that it’ll work properly with them.

Overriding Safety Mechanism

Safety mechanisms in our code shouldn’t be overridden when we write code. Risky changes should be minimized. The end might be lots of bugs and lots of debugging.

Turning off failing tests are bad and we should think about the possible consequences when we do.

Duplication

Code duplication is bad. Every time we have to change duplicate code, we have to change them in multiple places.

We can remove them by abstracting the code and putting them in a central location.

Coding becomes faster and less error-prone since we only have to change things in one place.

The most obvious form is clumps of identical code.

Another form is conditional statements that appear multiple times in different parts of the code. We can replace them with polymorphic code.

Most design patterns are well-known ways to eliminate duplication. They’re structured to eliminate them.

Code at Wrong Level of Abstraction

When we abstract code, we should make them completely. The separation is complete. All the higher-level concepts to be in the base class.

Constants, variables, and utility functions shouldn’t be in the base class.

Source files, components, and modules should at a higher level of abstraction also be separated from ones with lower levels of abstraction.

We don’t wrong higher-level and lower-level code mixed together.

For example, if we have an Account class:

class Account {  
  saveAccountToDb() {}  
  getAccount() {}  
  setAccount() {}  
}

Then we have code at 2 levels of abstraction because we have saveAccountToDb which is a helper method to save data to the database.

We want to move it to a helper class or function.

Conclusion

Comments should be minimized and are useful and up to date when they’re there.

Functions should have as few arguments as possible and only do one thing. This also means we shouldn’t have flag arguments.

When we write code, we should check for boundary and corner cases to avoid bugs. Also, we should override safety features like removing important tests. They’re probably there for a reason.

Finally, code duplication is bad.

Categories
JavaScript Best Practices

JavaScript Clean Code — More About Classes

Classes in JavaScript is syntactic sugar on top of the prototypical inheritance features of the language. However, in terms of writing clean code, the principles still apply since they have the same structure as classes in class-based languages.

In this article, we’ll look at how to write JavaScript classes in a clean and maintainable way.

We’ll look at how to organize for changes and using the class syntax instead of using constructor functions.

Organizing for Change

We have to prepare for classes to be changed when we organize them. This means that we should make them extendible rather than having to constantly modify code to get the functionality we want in our class.

Our methods should be simple. Simple methods are easier to test and we don’t have to change them as much.

We should follow the open/closed principle, which states that a piece of code should be open for extension but closed for modification.

This applies to classes just like another piece of code.

For example, if we have the following Rectangle class:

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

  get area() {
    return this.length * this.width;
  }
}

Then we can easily add a getter method for computing the perimeter of a rectangle as follows:

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

  get area() {
    return this.length * this.width;
  }

  get perimeter() {
    return 2 * (this.length + this.width);
  }
}

As we can see, we didn’t have to modify the existing code to add a method for computing the perimeter. We just add the perimeter getter method and be done with it.

Use the Class Syntax Instead of Constructor Functions

It’s time to move on to the class syntax instead of using constructor functions.

We can see why with the following example of inheritance:

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

function Employee(name, age, employeeCode) {
  Person.call(this, name, age);
  Employee.prototype.constructor = Person;
  this.employeeCode = employeeCode;
}

First, we have to create the Person constructor, then to make Employee ‘s prototype Person and set all the inherited properties, we have to first write:

Person.call(this, name, age);

to set all the instance variables, and:

Employee.prototype.constructor = Person;

to set the Employee’s prototype constructor to Person . We can easily miss any of these 2 lines and the Employee constructor won’t be inheriting from the Person constructor.

If we create an Employee instance as follows:

const employee = new Employee('Joe', 20, 'waiter');

Then we should see something like the following under the __proto__ property:

constructor: _ƒ Person(name, age)_

This means that we set the prototype of the Employee instance to the Person constructor correctly.

With the class syntax, we only have to use the extends keyword to inherit from one class. We can rewrite the code above as follows:

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

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

Then when we create the same Employee instance as follows:

const employee = new Employee('Joe', 20, 'waiter');

Then we should see something like the following under the __proto__ property:

constructor: _class Employee_

As we can see, both console.log outputs are the same, except for the function and class difference, but they’re the same since classes are the same as constructor functions.

However, we don’t have to use call or this , and set the variables of the superclass manually.

The JavaScript interpreter will tell us if we forgot to call super or use the extends keyword.

There’s no going back to the old constructor function syntax nowadays since it’s pretty inconvenient.

Conclusion

When we design classes, we should organize for change. This means that we should have code that’s open for extension but closed for modification.

This reduces the risk of messing up existing code why allowing us to keep making changes by adding new code.

Also, it’s time to move on to the class syntax for creating constructor functions. It’s hard to do inheritance with old constructor functions, while the class syntax makes everything much easier.

Categories
JavaScript Best Practices

JavaScript Clean Code: Concurrency

Concurrency is an important part of most modern programs. To achieve this in JavaScript, we have to use asynchronous code, which is non-blocking.

In this article, we’ll look at how to write asynchronous code in a way that’s clean and easy to read and change.


Use Promises Instead of Callbacks

Promises have been a standard object since ES6, so the previous asynchronous callbacks should all be replaced with promises.

Using callbacks is a real pain if we have any sequential code since we have to nest them on multiple levels.

For example, if we want to run multiple setTimeout callbacks without promises, then we have to nest them as follows:

setTimeout(() => {
  console.log('foo');
  setTimeout(() => {
    console.log('bar');
    setTimeout(() => {
      console.log('baz');
    }, 200)
  }, 200)
}, 200)

As we can see, we only have three callbacks and the nesting is already very ugly. We have to clean this up so that this is more pleasant to look at and easier to understand.

We can do this with promises as follows:

const timeOutPromise = (str) => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(str);
    }, 200)
  })
}
timeOutPromise('foo')
  .then((val) => {
    console.log(val);
    return timeOutPromise('bar');
  })
  .then((val) => {
    console.log(val);
    return timeOutPromise('baz');
  })
  .then((val) => {
    console.log(val);
  })

As we can see, with promises, we can chain them with the then method with a callback passed into it. We don’t have to nest callbacks except in the timeoutPromise function, and it’s only two levels of nesting instead of three or more.

We get the resolves value of a promise in the parameter of the callback that we pass into the then method.

To catch errors, we can use the catch method with a callback passed in as follows:

const timeOutPromise = (str) => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(str);
    }, 200)
  })
}
timeOutPromise('foo')
  .then((val) => {
    console.log(val);
    return timeOutPromise('bar');
  })
  .then((val) => {
    console.log(val);
    return timeOutPromise('baz');
  })
  .then((val) => {
    console.log(val);
  })
  .catch((err) => console.error(err))

Async/Await Is a Cleaner Syntax for Chaining Promises

ES2017 introduced the async and await syntax, which is a cleaner way of chaining promises.

We can rewrite what we had above by writing:

const timeOutPromise = (str) => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(str);
    }, 200)
  })
}

(async () => {
  let val;
  val = await timeOutPromise('foo');
  console.log(val);
  val = await timeOutPromise('bar');
  console.log(val);
  val = await timeOutPromise('baz');
  console.log(val);
})();

It’s exactly the same as:

const timeOutPromise = (str) => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(str);
    }, 200)
  })
}

timeOutPromise('foo')
  .then((val) => {
    console.log(val);
    return timeOutPromise('bar');
  })
  .then((val) => {
    console.log(val);
    return timeOutPromise('baz');
  })
  .then((val) => {
    console.log(val);
  })

The one difference is that the resolved value is assigned to val via the assignment operator. This assignment works as long as we have await before our promises.

To handle rejected promises, we can use the try...catch clause as we do with synchronous code:

const timeOutPromise = (str) => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(str);
    }, 200)
  })
}

(async () => {
  try {
    let val;
    val = await timeOutPromise('foo');
    console.log(val);
    val = await timeOutPromise('bar');
    console.log(val);
    val = await timeOutPromise('baz');
    console.log(val);
  } catch (err) {
    console.error(err);
  }
})();

async functions only return promises, so we can’t use them as general-purpose functions. They’re syntactic sugar for promises and not a replacement of it.


Conclusion

To write asynchronous code, promises are the way to go. They let us chain multiple of them together without nesting callbacks.

We should convert asynchronous code to promises if they aren’t returned as promises already. To chain them, we can use the then method.

To catch errors from rejected promises, we can use the catch method.

async and await are syntactic sugar for promises. They’re the same thing but async and await is shorter.

We can use try...catch to catch errors from rejected promises with async and await.

Categories
JavaScript Best Practices

JavaScript Clean Code — SOLID

Like any other programming language, JavaScript is also subject to the principles outlined in SOLID.

SOLID consists of 5 concepts that we can use to make our programs better. They are:

  • Single responsibility principle
  • Open / closed principles
  • Liskov Substitution principle
  • Interface segregation principle
  • Dependency inversion principle

In this article, we’ll look at each and see how we can apply them to JavaScript programs.

Single Responsibility Principle

The single responsibility principle says that each of our classes has to be only used for one purpose.

We need this so that we don’t have to change code as often when something changes. It’s also hard to understand what the class is doing if it’s doing many things.

Unrelated concepts in the same class also make comprehending the purpose of the code harder.

For example, we can write something like the following to follow the single responsibility principle:

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

  get area() {
    return this.length * this.width;
  }
}

The Rectangle class above only has the length and width of a rectangle as members and lets us get the area from it.

It does nothing else, so it follows the single responsibility principle.

A bad example would be:

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

  get area() {
    return this.length * this.width;
  }

  createCircle() {

  }
}

We should have a createCircle method in a Rectangle class since they’re unrelated concepts.

Open/Closed Principle

The open/closed principle states that a piece of software is open for extension but closed for modification.

This means that we should be able to add more functionality without changing existing code.

For example, if we have the following Rectangle class:

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

  get area() {
    return this.length * this.width;
  }
}

Then if we want to add a function to for calculating its perimeter, we can do it by adding a method to do it as follows:

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

  get area() {
    return this.length * this.width;
  }

  get perimteter() {
    return 2 * (this.length + this.width);
  }
}

As we can see, we didn’t have to change existing code to add it, which satisfies the open/closed principle.

Liskov Substitution Principle

This principle states that if we have a parent class and a child class, then we can interchange the parent and child class without getting incorrect results.

This means that the child class must implement everything that’s in the parent class. The parent class serves the class has the base members that child classes extend from.

For example, if we want to implement classes for a bunch of shapes, we can have a parent Shape class, which are extended by all classes by implementing everything in the Shape class.

We can write the following to implement some shape classes and get the area of each instance:

class Shape {
  get area() {
    return 0;
  }
}

class Rectangle extends Shape {
  constructor(length, width) {
    super();
    this.length = length;
    this.width = width;
  }

  get area() {
    return this.length * this.width;
  }
}

class Square extends Shape {
  constructor(length) {
    super();
    this.length = length;
  }

  get area() {
    return this.length ** 2;
  }
}

class Circle extends Shape {
  constructor(radius) {
    super();
    this.radius = radius;
  }

  get area() {
    return Math.PI * (this.radius ** 2);
  }
}

const shapes = [
  new Rectangle(1, 2),
  new Square(1, 2),
  new Circle(2),
]

for (let s of shapes) {
  console.log(s.area);
}

Since we override the area getter in each class that extends Shape , we get the right area for each shape since the correct code is run for each shape to get the area.

Interface Segregation Principle

The interface segregation principle states that “clients shouldn’t be forced to depend on interfaces that they don’t use.”

This means that we shouldn’t impose the implementation of something if it’s not needed.

JavaScript doesn’t have interfaces, so this principle doesn’t apply directly since it doesn’t enforce the implementation of anything via interfaces.

Dependency Inversion Principle

This principle states that high-level modules shouldn’t depend on low-level modules and they both should depend on abstractions, and abstractions shouldn’t depend upon details. Details should depend upon abstractions.

This means that we shouldn’t have to know any implementation details of our dependencies. If we do, then we violated this principle.

We need this principle because if we do have to reference the code for the implementation details of a dependency to use it, then when the dependency changes, there’s going to be lots of breaking changes to our own code.

As software gets more complex, if we don’t follow this principle, then our code will break a lot.

One example of hiding implementation details from the code that we implement is the facade pattern. The pattern puts a facade class in front of the complex implementation underneath so we only have to depend on the facade to use the features underneath.

If the underlying classes change, then only the facade has to change and we don’t have to worry about making changes to our own code unless the facade has breaking changes.

For example, the following is a simple implementation of the facade pattern:

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();
  }

}

We don’t have to worry about ClassA , ClassB and ClassC to implement the Foo class. As long as the Facade class doesn’t change, we don’t have to change our own code.

Conclusion

We should follow the SOLID principle to write code that’s easy to maintain.

To following SOLID, we have to write classes that only do one thing.

Our code has to open for extension but closed for modification. This reduces the chance of messing up the existing code.

Parent and child classes have to be interchangeable when we switch them. When we switch them, the result still has to be correct.

Finally, we should never have to depend on the implementation details of any piece of code that we reference so that we won’t end up with lots of breaking changes when something changes.

This lets us reduce the coupling between modules.

Categories
JavaScript Best Practices

JavaScript Clean Code: Emergent Design

Emergent design is a framework for systematic change. It focuses on delivering small pieces of working code with business value. It’s part of agile software development, which is more flexible than traditional software development.

In this article, we’ll look at the four principles of software development with emergent design.


What Are the Four Rules of Emergent Design?

The four rules of emergent design are simple. They’re:

  • Running all the tests.
  • Removing duplication.
  • Expressing the intent of the programmer.
  • Minimizing the number of classes and methods.

Running All the Tests

Running tests ensures that our system is still acting as expected after we made changes. The design might have been perfect on paper, but after doing the work, we may find unexpected flaws in the system. So we have to run tests to make sure that our changes are good.

We have to write code that’s easily testable. This means that the code should be loosely coupled so it can be easily tested by unit tests. Loose coupling also means that code can be more easily modified since it doesn’t affect other parts of the code.

We can use dependency inversion tools like dependency injection and abstractions to minimize coupling in JavaScript code.

Writing tests enforce low coupling and high cohesion since writing easily testable code requires code with these two characteristics.


Refactoring

Once we have tests, we can refactor without thinking too much about breaking things since we have tests to make sure that we don’t break anything.

We can apply the principles of good software design like SOLID when we refactor the code.

When we refactor, it’s time to choose better names, write smaller classes and methods, reduce the number of classes and methods, and write more expressive code.


Removing Duplicate Code

Duplicate code should be removed when we refactor code. Less duplicate code means less code to maintain and fewer places that we’ll forget to change.

Duplicate is not just code that is exactly the same. Their implementation can also be duplicated. For example, we can have a size and isEmpty methods for our own data structure.

They’re so similar that we don’t really need both. We can just make isEmpty return size() === 0;.

Code with duplicate implementation can be extracted into a common location. Anything that violates the single responsibility principle has to be extracted into its own class.

For example, let’s say we have a postal rate calculator to calculate international mailing rates as follows:

class PostalRateCalculator {
  calculateUSRates() {

  }

  calculateEURates() {

  }
}

We really don’t need two functions since the only difference between the two methods is the postal rates. Therefore, we can combine them into one method and use the different rates for different countries to calculate the rates as follows:

class PostalRateCalculator {
  calculateRates(country) {
    if (country === 'US') {

    } else if (country === 'UK') {

    }
    //...
  }
}

This way, we don’t have two methods that do similar things cluttering up the code.


Expressive Code

Expressive code means easy to understand code. Our code has to communicate our intentions clearly so that readers won’t misunderstand what we’re trying to do.

We can be expressive by choosing good names for everything.

Also, keeping a standard nomenclature for everything reduces the chance that we’ll be misled when other people read our code.

Well-written unit tests are also expressive. They should tell us a lot about what the program does since we have inputs and outputs all in one place. In addition, each test has its own description to tell us even more information about the part of the system that it’s testing.


Minimal Classes and Methods

If we don’t need the code, then we shouldn’t write it. Useless or duplicate code is just bad.

They clutter up our programs while providing little to no value.


Conclusion

There isn’t a clear cut way to use emergent to write our programs. However, it’s mostly down to refactoring using the clean code principles and then using unit tests to make sure everything still runs correctly.

Also, we have to write tests as if they’re documentation. Just like normal code, they have to be expressive.

Duplicate code or code with similar implementation can probably be merged into one.