Skip to content

JavaScript coding guidelines

Jaime Pastor edited this page Aug 2, 2021 · 3 revisions

JavaScript coding guidelines

adidas JavaScript coding guidelines are based on the Google JavaScript Style Guide with minor customization.

Global Conventions

List of main rules, customized ones and examples.

General styleguide

For readability purposes the general coding rules are compiled below.

Rule Reason
Statements
Use semicolon Clarity, detect end of statements
One statement per line Clarity, readability
Max one blank line between statements Readability, grouping
Use 2 spaces for indent Compatibility across systems
Use +1 indent for continuation Readability, consistency
Do not indent chains Grouping
Max line length 120 characters Try to keep the line line between 80 and 100 characters if possible
Ensure empty line at the end Compatibility across systems
Blocks
Open brace in the same line of keyword (function, class, method, conditional structure, object declaration, etc.) Style, consistency
Close brace in new line after block Style, consistency
Do not pad blocks with blank lines Optimize space, consistency
Comma expressions (declarations, objects, arrays, destructuring)
Comma at the end of expression Style, consistency
No extra comma on last expression Clarity, detect end of expressions
Strings
Use single quote Style, consistency
Use template instead of concatenation Style
Rule Value
Whitespace
Binary operators, assignment Spaces around operator
Unary operators No space between operator and identifier
Semicolon Space or new line after; no space nor new line before
Colon Space or new line after; no space nor new line before
Comma Space or new line after; no space nor new line before
Block, class, namespace, module curly braces Space before opening; no new line before opening; new line before closing
Import/export curly braces Space after opening brace; space before closing brace
Objects, arrays, destructuring, interpolate expression No space after opening; no space before closing
Function No spaces before parameters in named signature; no space before arguments in call
Arrow function Spaces around =>
Condition Space before opening
Ternary Spaces around question mark; spaces around colon
Inline comment / commented code Space after //
Doc comment Space after * ; space after tag

Example

import { readDir } from 'fs';
  
let a = 0,
  b, c;
  
/**
 * List contents of a directory async.
 *
 * @param {string} path - path to read.
 * @param {string} options.cwd - working directory.
 * @returns {Promise<Array<string>, Error>} promise handler.
 */
function asyncReadDir(path, { cwd = null }) {
  return new Promise((resolve, reject) => {
    function handler(error, data) {
      if (error) {
        reject(error)
      } else {
        resolve(data);
      }
    }
  
    readDir(path, { cwd }, handler);
  });
}
  
/**
 * List contents of a directory.
 *
 * @param {string} path - path to read.
 * @param {?string} cwd - current working directory.
 * @returns {Promise<Array<string>>} promise handler.
 */
export function listDir(path, cwd) {
  return asyncReadDir(path, { cwd })
  .then((data) => {
    console.info(`Found ${ data.length } items`);

    return data;
  })
  .catch((error) => {
    console.warn(error);

    // fail safe with empty array
    return [];
  });
}

Naming conventions

All the descriptors and names in JavaScript are written using the next four formats. The regular expression is only showing regular characters, but numbers, and other special characters can also be used, as well as the _ leading a private member.

Format Regular expression
camelCase /^[a-z][a-zA-Z]*&/
PascalCase /^[A-Z][a-zA-Z]*&/
kebab-case /^[a-z](-?[a-z])*&/
UPPER_SNAKE_CASE /^[A-Z_]*&/
Naming rule Format
Files and directories in scope of JavaScript kebab-case
Classes PascalCase
Named functions, parameters, class members and object properties camelCase
Variables (leading underscore can be used, trailing numbers should be avoided in general) camelCase
Constants (booleans, strings and numbers) UPPER_SNAKE_CASE
Module names camelCase
Good practice Reason
Use meaningful identifiers Clarity, readability
Avoid abbreviations Clarity, readability
Avoid single character identifiers in general (but indexes) Clarity, readability
Variable names have to be a noun or a group working as a noun Understandability
Array variables have to satisfy variable rules but in plural form Understandability
Boolean variable names have to be an adjective or verb in participle form or a group working as an adjective Understandability
Function names have to start with a verb Understandability

Example

class Worker {
  constructor(title, tasks) {
    this.title = title;
    this.parallelTasking = false;
    this.tasks = tasks;
  }
 
  startTask() {
    const MAX_TASKS_IN_QUEUE = 999;
    const _tasks = this.tasks.map((task) => task.name);
    let first = _tasks.pop();
    
    // TODO
  }
  
  stopTask() {
    // TODO
  }
}

const tasks = [
  { name: 'task-a' },
  { name: 'task-b' }
];
const worker = new Workder('title', tasks);
worker.startTask();

Conditional structures

Rule Reason
Do not use constant conditions Prevent infinite loops and dead branches
Join nested if-else clauses as if-elseif-else Unless there is a good reason, helps with readability
End do-while with semicolon after condition Use semicolon to terminate statements
Indent case clause on same column of switch keyword Case clauses are labels, not blocks. (No block, no indentation)
Ensure break after case Prevent unexpected fallthrough cases
Always add default case to switch cases Handling unexpected conditions might help to catch errors

Example

  • Conditions (if/else):
    // correct
    if (condition) {
      ...
    }
     
    if (condition) {
      ...
    } else {
      ...
    }
    
    // illegal
    if(condition){
      ...
    }
    
    if ( condition ) {
      ...
    }
    
    if (condition) {
      ...
    }
    else {
      ...
    }
    
    if (condition)
    {
      ...
    }
    else
    {
      ...
    }
  • Loop expressions (while/for):
    // correct
    for (statements) {
      ...
    }
    
    while (statements) {
      ...
    }
    
    // illegal
    for(statements){
      ...
    }
    
    for ( statements ) {
      ...
    }
    
    for (statements)
    {
      ...
    }
    
    while(statements){
      ...
    }
    
    while ( statements ) {
      ...
    }
    
    while (statements)
    {
      ...
    }
  • Loop expressions (do while):
    // correct
    do {
      ...
    } while (condition);
    
    // illegal
    do{
      ...
    }while(condition);
    
    do {
      ...
    } while ( condition );
      
    do
    {
      ...
    } while (condition);
  • Switch expressions (switch):
    // correct
    switch (letter) {
    case 'a':
      ...
      break;
    default:
      ...
    }
    
    // illegal
    switch(letter){
    case 'a':
      ...
      break;
    default:
      ...
    }
    
    switch ( letter ) {
    case 'a':
      ...
      break;
    default:
      ...
    }
    
    switch (letter)
    {
    case 'a':
      ...
      break;
    default:
      ...
    }
    
    switch (letter) {
    case 'a':
      ...
      break;
    }
    
    switch (letter) {
      case 'a':
        ...
        break;
      default:
        ...
    }

Inline comments

// some inline comment
Rule Reason
Do not abuse inline comments Too many comments might imply excessive complexity in the code, if possible, the code should be understandable by itself
Do not abuse TODO, FIXME or FUTURE comments Most of these will never be checked again
// correct
function foo() {
  // some comment
  ...
}

// illegal
function foo() {
  /*
   * some comment
   */
  
  /* some comment */
  ...
}

Documentation

/**
 * Description.
 * @param {ParamType} param - Parameter description.
 * @returns {ParamType} Description.
 */

The JavaScript documentation is based on @use JSDoc format.

Rule Reason
Set optional/defaults for parameters as required Documentation, completion
All parameters must be documented with type, name and description: use @param Clarity
Returns must be documented with type and description: use @returns Clarity
All documentation must have a description Clarity
Description should be separated from tags by one blank line Readability
Exported and internal functions must be documented, skip block scoped Clarity, interoperability, completion

Example

// correct
/**
 * Function description.
 * @param {?number} total - The total value of elements.
 * @returns {number} The average value.
 */
function someFunction(foo) {
  ...
}

class {
  /**
   * Attribute description.
   * @type {string}
   */
  someAttribute;
  
  /**
   * Method description.
   * @param {number} [foo=3] - a value.
   * @returns {number} a new value.
   */
  public someMethod(foo) {
    ...
  }
}

Commented code

// return execute(parameter, 'always')
// .then((result) => {
//   return get(result);
// });
Rule Reason
Commented code is now allowed in repositories Commented code is most definitely dead code, if not necessary, then remove it

Note: if there is a important reason in order not to remove the commented code, it has to be added to the commented block with a TODO.

// TODO: activate when the the `always` type is ready.
// return execute(parameter, 'always')
// .then(processResult)
// .catch((error) => {
//   // TODO: error management
//   console.warn('Error: ', error);
// });

New JavaScript versions

Rules applied to the new features added to the newest versions of JavaScript.

Note: remember that from ES6 onwards, they require transpilation to ES5 if the code has to run in a browser. For NodeJS JavaScript code it is not necessary.

ES5

Features that only apply to ES5.

Variables

var PI = 3.14;
var foo = new Foo();
Rule Reason
Always declare variables with var Non declared variables pollute the global window object
Do not use single var declaration Clarity
Initialize one variable per line Readability
A list of non-initialized variables can be grouped inline Readability, optimize space
Example
// correct
function foo() {
  var logger = new Logger();
  var index = 0;
  var variableName, anotherVariableName;
}

// illegal
function foo() {
  var logger = new Logger(),
    index = 0,
    aVariable, anotherVariable;
}

ES6 - EcmaScript 2015

Variables

const PI = 3.14;
const list = [];
let variableName = 'a variable which can be reassigned';
Rule Reason
Use let and const instead of var var causes hoisting which can be prevented with block scoped declarations
Do not group let/const declarations; sort: const declarations, let declarations Clarity
Initialize one variable per line Readability
A list of non-initialized variables can be grouped inline Readability, optimize space
No const without initialization Error
Example
// correct
function foo() {
  const PI = 3.14;
  const list = [];
  let variableName = 'a variable which can be reassigned';
}

// illegal
function foo() {
  const PI = 3.14,
    list = [];
  let variableName = 'a variable which can be reassigned', anotherVariable;
}

Functions

function foo(fn) {
  ...
}

foo(() => {
  ...
});

Rule of thumb:

  • Named, prototype or exported: function.
    • Short way inside classes and objects.
  • Callback: arrow function (=>).
Rule Reason
Use function keyword for named, prototype or exported functions Easier to spot and reuse; this is accessible within prototype
Use arrows for callbacks Contextual this and readability
Functions should not have state Pure functions are recommended as functional programming best practices
Spread operator can be used as needed Utility, prevent usage of arguments
Use parenthesis around arrow parameters Consistency
Use immediate return, in arrow function, whenever possible, and wrap returned objects with parenthesis Utility, readability, optimize space
Use default parameters as needed Utility
Use optional parameters as needed Utility
Example
  • Functions:
    // correct
    function foo() { ... }
    export function doSomething() { ... }
    export default function doMoreThings() { ... } 
    
    // illegal: named arrows
    const foo = () => { ... };
    export const doSomething = () => { ... };
    const doMoreThings = () => { ... };
    export default doMoreThings;
    
    // illegal: function assignment
    const foo = function() { ... };
    export const doSomething = function() { ... };
    const doMoreThings = function() { ... };
    export default doMoreThings;
  • Arrow functions:
    // correct
    someAsyncMethod((parameter) => {
      // TODO
    });
    someAsyncMethod(() => ({ some: 'object' }));
    someAsyncMethod(() => someStatement);
    someAsyncMethod((...args) => {
      console.log(args); // list of arguments
    });
    someAsyncMethod(() => {
      this.prop = 'value'; // accessing parent context
    });
    
    // illegal
    someAsyncMethod(() => {
      return { some: 'object' };
    });
    someAsyncMethod(() => {
      return someStatement;
    });
    someAsyncMethod(function() { // TODO });
    someAsyncMethod(function callback() { // TODO });
    someAsyncMethod(event => { // TODO });
    someAsyncMethod(() => {
      console.log(arguments); // `arguments` is not defined
    });
    
    const _this = this;  // preserve context through var assign
    someAsyncMethod(() => {
      _this.prop = 'value';
    });

For further information about functions and arrow functions see this StackOverflow thread.

Template strings

const person = { name: 'name' , age: 99, projects: [] };
const description = `The employee ${ person.name } is ${ person.age } years old`;
Rule Reason
Use template string instead of concatenation Style and performance
Example
// correct
const description = `The employee ${ person.name } works in ${ projects.join(', ') }`;

// illegal
const description = 'The employee ' + person.name + ' works in ' + projects.join(', ');

Import and export

import { Item } from 'module';
export { Item }
Rule Reason
Third party imports on top Clarity
Group imports from same module Clarity
Wildcard imports are allowed Some modules must be used this way
Avoid default export Default export is not consistent across systems and could cause trouble

Sort of imports order is neither checked nor enforced, yet this is the preferred sorting:

  • External module dependencies.
  • Local parent dependencies.
  • Local internal dependencies.
Example
// correct
import { Component } from 'react';
import { find, get, map } from 'lodash';
import { calculateAge } from '../modules/people/Age';
import { Person } from './Person';

export { Person };

// illegal
import { Person } from './Person';
import { calculateAge } from '../modules/people/Age';
import * as React from 'react';
import { find } from 'lodash';
import { get } from 'lodash';
import { map } from 'lodash';

export { property: value };

Classes

class ClassName {
  _field;

  static get staticField() {
    ...
  }

  constructor() {
    this.property = 'value';
  }

  doSomething() {
    ...
  }
}
Rule Reason
Prefix private members with _ Convention, readability, completion
Declare a single class per file Clarity, SRP
Remove empty constructors Optimize space
Use static accessors for static fields Convention

Sort members by C# style guide:

  • Members: fields > constructors > methods.
  • Visibility within members: public (default) > private.
  • Modifiers within visibility: static > instance (default).
Example
class Foo {
  someProp;
  _anotherProp = false;

  constructor(someProp) {
    this._someProp = someProp || 'defaultValue';
  }

  static get staticProp() {
    ...
  }

  doSomething() {
    ...
  }

  doSomething(param) {
    ...
  }

  _doSomethingElse() {
    ...
  }
}

ES7 - EcmaScript 2016

New features:

  • Array.prototype.includes.
  • Decorators.
  • Exponentiation operator: **.

ES7 new features work well with existing rules hence no additional rules are required.

See Babel configuration in the package Babel preset ENV.

ES8 - EcmaScript 2017

New features:

  • Shared memory and atomics.
  • String padding: padStart/padEnd.
  • Object methods: values/entries, getOwnPropertyDescriptors.
  • Support trailing commas in function parameters, lists and calls.
  • Asynchronous code with async/await.

Most ES8 features work well with existing rules but:

  • To keep consistency with previous rules, trailing commas are still discouraged.

See Babel configuration in the package Babel preset ENV.

JSX - JavaScript Extension

See Babel preset React.

Linting tools

In order to check most of the style rules, there are available configurations for JavaScript and its different versions using ESLint linting tool.

All of the configurations are in the repository js-linter-configs, and they are available via NPM as separated modules:

Links of interest