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
forparentheses (repeatconstorletfor each variable in a separate statement) - Give descriptive names
- Do not use similar names or synonyms for different variables unless following a convention
for...initerators should use descriptive namesforiterators 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
forparentheses, unless already defined - Iterator variables should be named
iif possible. Nestedforloops usej,k, etc. - Use
forwith arrays,for...infor 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
selffor passingthisinto 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 instanceofto 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 therequiresection
- 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
internalsnamespace) and called at the top of the module after theinternalsdeclaration.
// 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
erris reserved for errors received via a callback. Useerrorfor local function variables
Callback
- First argument must always be
err - Inline callbacks must use arrow functions
- If a function takes a
callbackargument, it must be called onprocess.nextTick(). Otherwise, the argument name must benextto 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