Coding Conventions
JavaScript
Language
Strict Mode
- The first line of every file should be
'use strict';
. If the file contains a shebang, strict mode should be enabled on the second line. - The strict mode directive should be followed by a blank line.
// Right
'use strict';
console.log('even when not required');
// Also right
#!/usr/bin/env node
'use strict';
console.log('even when not required');
// Wrong
'use strict';
console.log('even when not required')
Semicolon
- Always end statements with
;
// Right
console.log('even when not required');
// Wrong
console.log('even when not required')
Variable declarations
- Any variable that is only assigned once should be defined using
const
. - Any variable that is assigned multiple times should be defined using
let
. - Variables should not be declared using
var
. - Declare on first use, not at top of function; self being an exception
- Do not chain declarations unless inside
for
parentheses (repeatconst
orlet
for each variable in a separate statement) - Give descriptive names
- Do not use similar names or synonyms for different variables unless following a convention
for...in
iterators should use descriptive namesfor
iterators should use single character names- Use combination of plural for array and singular for each item in the array
- Use camelCase, never underscores
- Avoid using numbered variables (e.g. i1, i2, i3)
Scope
- No implicit or single statement scopes
- All scopes must be wrapped in
{}
// Right
if (condition) {
return;
}
// Wrong
if (condition) return;
if (condition)
return;
For loops
- Iterator variable should be declared inside the
for
parentheses, unless already defined - Iterator variables should be named
i
if possible. Nestedfor
loops usej
,k
, etc. - Use
for
with arrays,for...in
for objects (and always checkhasOwnProperty()
) - Always
++i
, neveri++
// Right
const name = 'john';
for (let i = 0; i < name.length; ++i) {
console.log(name[i]);
}
// Wrong
let position;
const name = 'john' ;
const len = name.length;
for (position = 0; position < len; position++) {
console.log(name[position]) ;
}
Prototype members
- Prefix private members with
_
Example.prototype.method = function () {
this.public = 'external';
this._private = 'internal';
};
- Define
self
for passingthis
into nested functions
Example.prototype.method = function () {
const self = this;
call(123, function (err) {
self.display(err);
});
};
Function declaration
- Declare functions via assignment
- Arrow function arguments must be enclosed in parentheses
- Arrow function bodies must be enclosed in curly braces
// Right
const method = function () {
};
const arrow = (foo) => {
return bar;
};
// Wrong
function method() {
}
const arrow = foo => bar;
Enforcing new on Constructor
- Use
this instanceof
to check if a constructor function was called with new. (This allows for future prototypical inheritance.)
Hoek.assert(this instanceof Server, 'Server must be instantiated using new');
Style
Whitespace
- Always spaces, never tabs
- 4 spaces indents
- No trailing whitespace at end-of-line
// Right
if (test) {
if (value === 12) {
console.log('result');
}
}
// Wrong
if (test) {
if (value === 12) {
console.log('result');
}
}
String literals
- Always
'
never"
// Right
const string = 'text in single quotes';
// Wrong
const string = "text in single quotes";
Newlines
all files need to end with a newline (or more accurately end of line). IDEs will often do a line separator instead. This is to ensure it is unix friendly. The "cat" command is a good example of seeing this behavior. Git does a good job of pointing these out when doing pull requests.
Two empty lines between module functions or assignments (end of function to comment about next function)
exports.get = function () {
// Some code
};
// 1
// 2
/**
* jsDoc comment
*/
internals.utility = function () {
//Some code
};
- Newline after
{
except for inlined or empty objects- Inline an object when it improves readability and unlikely to change often
- No inline object in assignment unless empty
// Right
if (condition) {
execute(value, { strict: true });
}
if (condition) {
const options = {
strict: true
};
execute(value, options);
}
const empty = {};
// Wrong
if (condition) { execute(value, { strict: true }); }
if (condition) {
const options = { strict: true };
execute(value, options);
}
const empty = {
};
- Newline after
}
- Only exception is when followed by
,
,;
,);
which must be followed by a newline - Includes before
else
,catch
, etc. - Empty line after
}
if not last statement in scope
- Only exception is when followed by
// Right
if (condition) {
value = {
func: () => {
console.log('example');
},
message: 'hello'
};
execute(value, (err) => {
console.log(err);
});
}
else {
console.log('otherwise');
}
// Wrong
if (condition) {
value = {
func: () => {
console.log('example');
}, message: 'hello'
};
execute(value, (err) => {
console.log(err); }
);
} else {
console.log('otherwise');
}
- Empty line after
{
- Following a multi-line condition
- In function scope declarations
- In arrow function declarations using curly braces
// Right
exports.method = function () {
if (condition) {
if (otherCondition) {
console.log('sometimes');
}
if (result &&
result.statusCode === 200) {
console.log('special case');
}
console.log('always');
}
execute(123, (err) => {
console.log(err);
});
const empty = {};
};
// Wrong
exports.method = function () {
if (condition) {
if (otherCondition) {
console.log('sometimes');
}
if (result &&
result.statusCode === 200) {
console.log('special case');
}
console.log('always');
}
execute(123, (err) => {
console.log(err);
});
const empty = {
};
};
- No empty line before end of scope
// Right
if (condition) {
if (otherCondition) {
console.log('done');
}
}
// Wrong
if (condition) {
if (otherCondition) {
console.log('done');
}
}
Spaces
- Use one and only one space (when required)
// Right
const value = calculate(1, 3);
// Wrong
const value = calculate(1, 3);
- One space between function and
(
when declaring a function
// Right
const example = function () {
return value;
};
// Wrong
const example = function() {
return value;
};
- No space between function name and
(
when invoking a function
// Right
const key = example();
// Wrong
const key = example ();
- No space after
(
or before)
// Right
execute('order', 34);
if (result === 'ok') {
console.log('success');
}
// Wrong
execute( 'order', 34 );
if ( result === 'ok' ) {
console.log( 'success' );
}
- No space before object key
:
, always after object key:
// Right
const obj = {
a: 1,
b: 2,
c: 3
};
// Wrong
const obj = {
a : 1,
b :2,
c:3
};
- No space before
;
, always after;
if not end-of-line
// Right
const name = 'john';
for (let i = 0; i < name.length; ++i) {
console.log(name[i]);
}
// Wrong
const name = 'john' ;
for (let i = 0;i < name.length ;++i) {
console.log(name[i]) ;
}
- Always space after reserved keywords (
if
,else
,for
,return
,function
, etc.)
// Right
for (let book in books) {
if (books.hasOwnProperty(book)) {
console.log(book.name);
}
}
// Wrong
for(let book in books) {
if(books.hasOwnProperty(book)) {
console.log(book.name);
}
}
- Always space after
{
and before}
in inlined object- No space for empty objects
{}
- One space for empty functions
{ }
- No space for empty objects
// Right
execute({ name: 'john', email: 'john@example.com' });
const empty = {};
const callback = () => { };
// Wrong
execute({name: 'john', email: 'john@example.com'});
const empty = { };
const callback = () => {};
- No space after
[
and before]
in inlined arrays
// Right
const numbers = [1, 2, 3];
// Wrong
const numbers = [ 1, 2, 3 ];
- Always space after
//
// Right
// Some comment
// Wrong
//Some comment
- No space before
,
, always after,
unless end-of-line
// Right
const numbers = [1, 2, 3];
execute({ name: 'john', email: 'john@example.com' });
for (let i = 0; i < name.length; ++i) {
console.log(name[i]);
}
// Wrong
const numbers = [1,2 ,3];
execute({ name: 'john',email: 'john@example.com' });
// This for loop violates the style guide, but illustrates incorrect spacing around a comma
for (let i = 0,il = name.length; i < il; ++i) {
console.log(name[i]);
}
- Always space before and after operators, unless following an indent or end-of-line
// Right
const a = 1 + 3;
const b = 'john' +
' ' +
'doe';
// Wrong
const a=1+3;
const b='john'+
' '+
'doe';
Commas
- Never begin a line with
,
(always at the end of the previous line)
// Right
execute('some error message',
12345,
this);
// Wrong
execute('some error message'
,12345
,this);
Operators
- Never begin a line with an operator (always at the end of the previous line)
// Right
const message = 'Hello ' +
'Steve, ' +
'How are you?';
if (value === 'hello' &&
result === 'ok') {
console.log('yes');
}
// Wrong
const message = 'Hello '
+ 'Steve, '
+ 'How are you?';
if (value === 'hello'
&& result === 'ok') {
console.log('yes');
}
Ternary Operators
- Never begin a line with a ternary operator. If a ternary statement must wrap, indent it further than the previous line by 4 spaces.
// Right
const message = foo === bar ?
foo :
bar;
// Wrong
const message = foo === bar
? foo
: bar;
// Also Wrong
const message = (foo === bar ?
foo :
bar);
Comments
Always use
//
unless it's a jsDoc declaration or license headerAlways begin sentences with an upper case
No trailing
.
unless comment contains multiple sentencesFormal style, consistent voice, no humor, present tense
No developer name or other personal notes
No TODOs
Line
- Provides narrative for the following single code line (or single statement broken for readability)
- One line of comment only
- One empty line before and none after the comment line
- No empty line before when following
{
unless other rules require it
function execute() {
// Initialize state
const position = 0;
if (condition) {
// Return message
return 'hello';
}
}
- Segment
- Provides narrative for the following code section (one or more lines of code, with or without line breaks)
- One or more lines of comments
- One empty line before and one after comments block
function execute() {
// Print each book's name
for (let book in books) {
// Check for valid properties
if (books.hasOwnProperty(book)) {
console.log(book.name);
}
}
}
- Note
- Explains the behaviour of a single code statement (can be broken into multiple lines)
- Used to document unexpected behaviour or non-standard practice
- Appears immediately at the end of the line (or statement) it describes, following whitespace to separate it from code block
function execute(value) {
if (value !== null &&
value !== undefined) { // Explicit check as 'value' can be 0
console.log(value);
}
}
Multi-line statements
Statements should only be broken into multiple lines to improve readability
Break statements if they are longer than 150 characters long
No empty lines in the middle of a single statement
Indent multi-line statements
Conditions should be indented to the first character of the condition in the first line
if (result &&
result.status &&
result.status.statusCode === 200) {
console.log('success');
}
- Variable should be indented to the first character of the value in the first line
const message = 'hello' +
' and welcome';
Node
Require
- Use uppercase variable names for imported modules
- All require statements must be declared at the top of the module
- Always use relative paths
Module globals
- Every module can only have two top level globals (except for imported modules):
exports
- defined automatically by nodeinternals
- must be declared as an object at the top of each module immediate following therequire
section
- Any variable global to the module must be a property of
internals
, including constants - If a module has automatically executing code, it must be contained within a function (using the
internals
namespace) and called at the top of the module after theinternals
declaration.
// Right
const Hapi = require('hapi');
const Hoek = require('hoek');
const Package = require('./package.json');
const internals = {
foo: 'bar'
};
internals.init = function () {
const server = new Hapi.Server();
...
};
internals.init();
// Also right
const Hapi = require('hapi');
const internals = {};
internals.package = require('./package.json');
internals.foo = 'bar';
internals.init = function () {
const server = new Hapi.server();
...
};
internals.init();
// Wrong
const hapi = require('hapi'); // Use uppercase name
const foo = 'bar'; // No global vars outside of internals
const internals = {
Foo: 'bar' // Don't use uppercase vars inside internals
};
const server = new Hapi.Server(); // No global vars outside of internals and exports / Set up your module inside an init() function
...
const Hoek = require('hoek'); // Declare modules at the top of the module
Variable names
err
is reserved for errors received via a callback. Useerror
for local function variables
Callback
- First argument must always be
err
- Inline callbacks must use arrow functions
- If a function takes a
callback
argument, it must be called onprocess.nextTick()
. Otherwise, the argument name must benext
to clearly declare that it may get called on same tick - Callbacks should always be called with explicit
return
Promises
- Public interfaces should (not must) return a promise when no callback is provided
- Promises should not be used internally
- Only native promises are allowed. Third party promise implementations are not allowed