An evolving set of best practices for writing maintainable Javascript and CSS
Use const
instead of var
for all of your variable declarations that will not be reassigned .
✅ Do
const a = 1;
const b = 2;
🚫 Don't
var a = 1;
var b = 2;
To reassign references, use let
instead of var
.
✅ Do
let count = 1;
if (true) {
count += 1;
}
🚫 Don't
var count = 1;
if (true) {
count += 1;
}
Declare variables separately as individual statements where each has its own line. Chaining variable declarations together with trailing commas is brittle and error prone.
✅ Do
const firstVar;
const secondVar;
const thirdVar;
🚫 Don't
const header,
topnav,
content;
Avoid single letter names for variables, functions, and object properties.
🚫 Don't
const a = 1;
const b = () => {};
const c = {
d: false,
e() {}
};
A variable's name should be descriptive so it indicates how/where it is used and/or clearly communicates the significance of the data it references.
✅ Do
const age = 20;
const isAdult = age => {
return age >= 18;
};
🚫 Don't
const val = 20;
const calc = data => {
return data >= 18;
};
The name of a function or method should clearly communicate intended functionality. The name should indicate what the function does, not how it is done.
✅ Do
const robot = {
greeting: 'Hello master!',
greet() {
alert(this.greeting);
}
};
🚫 Don't
const robot = {
phrase: 'Hello master!',
talk() {
alert(this.phrase);
}
};
Place 1 space before the leading brace.
✅ Do
function test() {
console.log('test');
}
dog.set('attr', {
age: '1 year',
breed: 'Bernese Mountain Dog',
});
🚫 Don't
function test(){
console.log('test');
}
dog.set('attr',{
age: '1 year',
breed: 'Bernese Mountain Dog',
});
Place 1 space before the opening parenthesis in control statements (if
, while
, for
, etc...). Place no space between the argument list and the function name in function calls and declarations.
✅ Do
if (isJedi) {
fight();
}
function fight() {
console.log('Swooosh!');
}
🚫 Don't
if(isJedi) {
fight ();
}
function fight () {
console.log ('Swooosh!');
}
Set off operators with spaces.
✅ Do
const x = y + 5;
🚫 Don't
const x=y+5;
Add a space after each curly brace when using the destructuring assignment syntax with objects.
✅ Do
const { inputs, outputs } = txn;
🚫 Don't
const {inputs, outputs} = txn;
Do not pad your blocks with blank lines.
🚫 Don't
function bar() {
console.log(foo);
}
if (baz) {
console.log(qux);
} else {
console.log(foo);
}
class Foo {
constructor(bar) {
this.bar = bar;
}
}
✅ Do
function bar() {
console.log(foo);
}
if (baz) {
console.log(qux);
} else {
console.log(foo);
}
Use named exports instead of default exports when possible.
✅ Do
export const foo = () => {};
const bar = () => {};
export bar;
Only import from a path in one place.
✅ Do
import foo, { named1, named2 } from 'foo';
import foo, {
named1,
named2,
} from 'foo';
🚫 Don't
import foo from 'foo';
// … some other imports … //
import { named1, named2 } from 'foo';
Put all import
statements above non-import statements.
✅ Do
import { foo } from 'foo';
import { bar } from 'bar';
foo.init();
🚫 Don't
import { foo } from 'foo';
foo.init();
import { bar } from 'bar';
Multiline imports should be indented just like multiline array and object literals.
✅ Do
import {
longNameA,
longNameB,
longNameC,
longNameD,
longNameE,
} from 'path';
🚫 Don't
import { longNameA, longNameB, longNameC, longNameD, longNameE } from 'path';
Use dot notation when accessing properties.
✅ Do
const luke = {
jedi: true,
age: 28,
};
const isJedi = luke.jedi;
🚫 Don't
const luke = {
jedi: true,
age: 28,
};
const isJedi = luke['jedi'];
Use bracket notation []
when accessing properties with a variable.
✅ Do
const luke = {
jedi: true,
age: 28,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');
Use ===
and !==
over ==
and !=
to avoid unintended coercions by the ToBoolean
abstract method that evaluates expressions in JavaScript.
- Objects evaluate to true
- Undefined evaluates to false
- Null evaluates to false
- Booleans evaluate to the value of the boolean
- Numbers evaluate to false if +0, -0, or NaN, otherwise true
- Strings evaluate to false if an empty string
''
, otherwise true
// objects, empty or not, evaluate to true
if ({}) {
// true
}
// an array (even an empty one) is an object
if ([0] && []) {
// true
}
Use shortcuts for booleans, but explicit comparisons for strings and numbers.
✅ Do
if (isValid) {
// ...
}
if (name !== '') {
// ...
}
if (collection.length > 0) {
// ...
}
🚫 Don't
if (isValid === true) {
// use a comparison shortcut for booleans
}
if (collection.length) {
// ...
}
if (name) {
// can unexpectedly evaluate to truthy
// use explicit comparison instead
// see example below
}
// Example: unexpected truthy evaluation for a string
// Result: alerts "It's true"
const name = ' ';
if (name) {
alert("It's true");
} else {
alert("It's false")
}
When writing a switch statement, use curly braces to create block scope for each case
and default
clause that contain lexical declarations. When each case
/default
clause has its own block scope, it prevents unintentional pollution of the namespace with duplicate lexical declarations.
✅ Do
switch (foo) {
case 1: {
let x = 1;
break;
}
case 2: {
let x = 2;
break;
}
case 3: {
const y = 1;
function f() {
alert(y);
}
break;
}
case 4:
const y = 2;
function f() {
alert(y);
}
break;
default: {
class C {}
}
}
🚫 Don't
switch (foo) {
case 1:
let x = 1;
break;
case 2:
let x = 2;
break;
case 3:
const y = 1;
break;
default:
const y = 2;
class C {}
}
Ternaries should not be nested and generally be single line expressions.
🚫 Don't
const foo = maybe1 > maybe2
? "bar"
: value1 > value2 ? "baz" : null;
✅ Do
// split into 2 separated ternary expressions
const maybeNull = value1 > value2 ? 'baz' : null;
// better, but still uses multiple lines
const foo = maybe1 > maybe2
? 'bar'
: maybeNull;
// single line is best
const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
Avoid unneeded ternary statements.
🚫 Don't
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;
✅ Do
const foo = a || b;
const bar = !!c;
const baz = !c;
When mixing operators, enclose them in parentheses. The only exception is the standard arithmetic operators (+
, -
, *
, & /
) since their precedence is broadly understood.
✅ Do
const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
const bar = (a ** b) - (5 % d);
if (a || (b && c)) {
return d;
}
const bar = a + b / c * d;
🚫 Don't
const foo = a && b < 0 || c > 0 || d + 1 === 0;
const bar = a ** b - 5 % d;
// one may be confused into thinking (a || b) && c
if (a || b && c) {
return d;
}
Use braces with all multi-line blocks.
✅ Do
if (test) return false;
if (test) { return false; }
if (test) {
return false;
}
function bar() {
return false;
}
🚫 Don't
if (test)
return false;
If you’re using multi-line blocks with if
and else
, put else
on the same line as your if
block’s closing brace.
✅ Do
if (test) {
thing1();
thing2();
} else {
thing3();
}
🚫 Don't
if (test) {
thing1();
thing2();
}
else {
thing3();
}
If an if
block always executes a return
statement, the subsequent else
block is unnecessary. A return
in an else if
block following an if
block that contains a return
can be separated into multiple if
blocks.
✅ Do
function foo() {
if (x) {
return x;
}
return y;
}
function cats() {
if (x) {
return x;
}
if (y) {
return y;
}
}
function dogs(x) {
if (x) {
if (z) {
return y;
}
return q;
}
return z;
}
🚫 Don't
function foo() {
if (x) {
return x;
} else {
return y;
}
}
function cats() {
if (x) {
return x;
} else if (y) {
return y;
}
}
function dogs() {
if (x) {
return x;
} else {
if (y) {
return y;
}
}
}
If your control statement (if
, while
etc.) exceeds the maximum line length, each (grouped) condition could be put into a new line. The logical operator should begin the line.
- Requiring operators at the beginning of the line persists alignment and follows a pattern similar to method chaining. This visual improvement assists the reader in following complex logic when reading the statement.
✅ Do
if (
foo === 123
&& bar === 'abc'
) {
thing1();
}
if (
(foo === 123 || bar === 'abc')
&& doesItLookGoodWhenItBecomesThatLong()
&& isThisReallyHappening()
) {
thing1();
}
if (foo === 123 && bar === 'abc') {
thing1();
}
🚫 Don't
if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
thing1();
}
if (foo === 123 &&
bar === 'abc') {
thing1();
}
if (foo === 123
&& bar === 'abc') {
thing1();
}
if (
foo === 123 &&
bar === 'abc'
) {
thing1();
}
When a function contains a control statement, always return early if the return value is known or null
.
✅ Do
function meetsMinimum(amount) {
// return early
if (!amount) return;
return amount >= 1;
}
🚫 Don't
function txnIsPending(txn) {
if (txn && txn.status === 'creating') {
return true;
}
return false;
}
Use single quotes ''
.
✅ Do
const name = 'Ada Lovelace';
🚫 Don't
const name = "Mary Poppins";
const name = `Mary Poppins`;
Template string literals should contain interpolation of data or newlines.
✅ Do
const firstName = 'Ada';
const lastName = 'Lovelace';
const fullName = `${firstName} ${lastName}`;
const bio = `
My name is ${fullName}. I was born in London, England.
The year was 1815 and winter was upon us.
`;
🚫 Don't
const fullName = `Ada Lovelace`;
If your function takes a single argument, omit the parentheses for less visual clutter.
✅ Do
[1, 2, 3].map(x => x * x);
[1, 2, 3].map(number => (
`A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
));
🚫 Don't
[1, 2, 3].map((x) => x * x);
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
Enforce the location of arrow function bodies with implicit returns.
✅ Do
foo => bar;
foo => (bar);
foo => (
bar
)
🚫 Don't
foo =>
bar;
foo =>
(bar);
In case the expression spans over multiple lines, wrap it in parentheses for better readability.
✅ Do
['get', 'post', 'put'].map(httpMethod => (
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
));
🚫 Don't
['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
);
If the function body consists of a single statement omit the braces and use the implicit return.
✅ Do
[1, 2, 3].map(number => `A string containing the ${number}.`);
[1, 2, 3].map((number, index) => ({
[index]: number,
}));
-
Extensions: Use the
.js
extension for React components. -
Filenames: Use PascalCase for component filenames. 👉
TransactionsList.js
. -
Component Naming: Use the component name as the filename. For example,
TransactionsList.js
should contain a component namedTransactionsList
. -
HOC Naming: Use camelCase to name a higher-order component. Also, use the component name as the filename. For example,
withTheme.js
should contain an HOC namedwithTheme
. -
HOC displayName: To create the
displayName
of a higher-order component, combine the HOC's name with the passed-in component’s name. For example, if the HOC's name iswithTheme
and the passed-in component's name isTransactionsList
, the result is 👉withTheme(TransactionsList)
. -
Props Naming: Avoid using DOM component prop names for different purposes. People expect props like
style
andclassName
to mean one specific thing.
Follow these alignment styles for JSX.
🚫 Don't
<Foo superLongParam="bar"
anotherSuperLongParam="baz" />
✅ Do
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
/>
// if all props fit on one line, that's good!
<Foo bar="bar" />
🚫 Don't
{showButton &&
<Button />
}
{
showButton &&
<Button />
}
✅ Do
{showButton && (
<Button />
)}
// if the expression fits on one line, that's good!
{showButton && <Button />}
Always include a single space in your self-closing tag.
✅ Do
<Foo />
🚫 Don't
<Foo/>
<Foo
/>
<Foo />
Do not pad JSX curly braces with spaces.
🚫 Don't
<Foo bar={ baz } />
✅ Do
<Foo bar={baz} />
Always use double quotes ""
for JSX attributes.
Use single quotes ''
for all other JS.
✅ Do
<Foo bar="bar" />
<Foo style={{ left: '20px' }} />
🚫 Don't
<Foo bar='bar' />
<Foo style={{ left: "20px" }} />
Always use camelCase for prop names.
✅ Do
<Foo
userName="hello"
phoneNumber={12345678}
/>
🚫 Don't
<Foo
UserName="hello"
phone_number={12345678}
/>
Omit the value of the prop when it is explicitly true
.
✅ Do
<Foo hidden />
🚫 Don't
<Foo hidden={true} />
Filter out unused props when using the spread operator to pass props.
✅ Do
render() {
const { unusedProp1, unusedProp2, ...usedProps } = this.props;
return <WrappedComponent {...usedProps} />;
}
🚫 Don't
render() {
const { unusedProp1, unusedProp2, ...usedProps } = this.props;
return <WrappedComponent {...this.props} />;
}
When creating a flow type definition, define its properties in ABC order.
✅ Do
type Props = {
autoFocus: boolean,
autoResize: boolean,
className?: string,
context: ThemeContextProp,
disabled?: boolean,
label?: string | Element<any>,
error?: string | Node,
onBlur?: Function,
onChange?: Function,
onFocus?: Function,
};
For preventing syntax errors, leave a comma after the last key/value pair incase the type definition's properties are rearranged or expanded upon in the future.
✅ Do
type State = {
inputValue: string,
error: string,
isOpen: boolean,
composedTheme: Object,
};
🚫 Don't
type State = {
inputValue: string,
error: string,
isOpen: boolean,
composedTheme: Object
};
Wrap JSX tags in parentheses when they span more than one line. Multiline JSX should not start on the same line as a parentheses.
✅ Do
// single line
render() {
const body = <div>hello</div>;
return <MyComponent>{body}</MyComponent>;
}
// multiline
render() {
return (
<MyComponent foo="bar">
<MyChild />
</MyComponent>
);
}
// both
render() {
const body = (
<div>
<h1>Hello</h1>
</div>
);
return <MyComponent>{body}</MyComponent>;
}
// stateless single line
const Name = () => <h1>Bobby Smith</h1>;
// stateless multiline
const Names = () => (
<div>
<h1>Bobby Smith</h1>
<h1>Brenda Smith</h1>
</div>
);
🚫 Don't
render() {
return <MyComponent variant="long body" foo="bar">
<MyChild />
</MyComponent>;
}
render() {
return (<MyComponent variant="long body" foo="bar">
<MyChild />
</MyComponent>);
}
const Names = () => (<div>
<h1>Bobby Smith</h1>
<h1>Brenda Smith</h1>
</div>);
- Use class selectors instead of ID selectors.
- Use camelCase or dashed-case instead of PascelCase or names_with_underscores.
- Give each selector its own line.
- Put a space before the opening brace
{
in rule declarations. - Put closing braces
}
of rule declarations on a new line. - In properties, put a space after, but not before, the
:
character. - Put blank lines between rule declarations.
✅ Do
.avatar {
border-radius: 50%;
border: 2px solid white;
}
.one,
.selector,
.perLine {
// ...
}
🚫 Don't
.avatar{
border-radius:50%;
border:2px solid white; }
.no, .nope, .not_good {
// ...
}
#id_selector {
// ...
}
The properties of a selector should always be defined in ABC order.
✅ Do
.supportRequest {
bottom: 0;
position: absolute;
width: auto;
}
🚫 Don't
.title {
text-align: center;
cursor: default;
margin-bottom: 8.5px;
line-height: 1.2;
font-family: var(--font-regular);
font-size: 20px;
}
End every property declaration with a semicolon for consistency and extensibility.
✅ Do
.test {
display: block;
height: 100px;
}
🚫 Don't
.test {
display: block;
height: 100px
}
- Always use the
.scss
extension when creating a Sass file. - Nested selectors go last and nothing goes after them.
- Add whitespace between your rule declarations.
- Add whitespace between your nested selectors and between adjacent nested selectors.
- The properties of nested selectors should always be defined in ABC order.
✅ Do
.btn {
background: green;
font-weight: bold;
@include transition(background 0.5s ease);
.icon {
margin-right: 10px;
}
}
🚫 Don't
.btn {
width: 30px;
background: green;
font-weight: bold;
.icon {
margin-right: 10px;
}
@include transition(background 0.5s ease);
}
Define nested selectors in the same order they are applied to their associated HTML.
Example:
import React from 'react';
const Modal = () => (
<div className="modal"> /* --- 1st --- */
<h3 className="title">Your Modal</h3> /* --- 2nd --- */
<button className="btn">Accept</button> /* --- 3rd --- */
<span className="close">X</span> /* --- 4th --- */
</div>
);
✅ Do
.modal {
// comes 1st...
height: 300px;
width: 400px;
z-index: 1;
.title {
// comes 2nd...
}
.btn {
// comes 3rd...
}
.close {
// comes 4th...
}
}
🚫 Don't
.modal {
// comes 1st...
height: 300px;
width: 400px;
z-index: 1;
.btn {
// // comes 3rd...
}
.close {
// comes 4th...
}
.title {
// comes 2nd...
}
}
Do not nest selectors more than three levels deep. When selectors become this long, you're likely writing CSS that is:
- Strongly coupled to the HTML (fragile) -- or --
- Overly specific -- or --
- Not reusable
🚫 Don't
.pageContainer {
.content {
.profile {
// STOP!
}
}
}
- Begin a variable name with two dashes
--
. - Use dash-case CSS variable names (e.g.
$--font-regular
). - Do not use camelCase or snake_case variable names.
- Prefer line comments (
//
for Sass) to block comments. - Prefer comments on their own line. Avoid end-of-line comments.
- Write detailed comments for code that isn't self-documenting:
- Uses of z-index
- Compatibility or browser-specific hacks
- The Daedalus
master
branch is protected bydevelop
. Onlydevelop
is merged intomaster
. - A release branch contains the version in its name in this format
release/version
. Example: (release/0.12.0
). - Release branches are based from
master
, but their version specific changes are not merged intomaster
. - To make additions to the Daedalus codebase, create a branch based from
develop
. - There are three main branch types -->
feature
,chore
, andfix
. - A
feature
branch adds a new feature to Daedalus that does not yet exist. - A
chore
branch is appropriate when cleaning up or refactoring an area within the codebase. - A
fix
branch is used to introduce a fix for a bug.
- A branch prefix is a combination of its type (
feature
,chore
, orfix
) and its YouTrack ID. - YouTrack ID's for Daedalus start with the letters
DDW-
+ issue number. - A branch suffix is a concise description of the changes contained in the branch.
Examples:
feature/ddw-41-create-daedalus-best-practices-guide
chore/ddw-225-refactor-api-to-use-v1
fix/ddw-315-fix-wallet-stuck-on-syncing
Commit messages should begin with the YouTrack issue ID.
Example: git commit -m "[DDW-41] Adds Git section to Best Practices"
- All entries should be written in past tense, preferably be 1 sentence in length, and ordered from most recent to least recent using their PR's time of creation.
- The CHANGELOG.md document is organized into sections by release number.
- Each release section contains 3 subsections titled to align with the branch types:
features
,chores
,fixes
. - Add your entry to the appropriate subsection based on its content.
- Add 1 entry per PR and include a URL to the PR at the end of your entry.
- Example:
- Reduced resource usage ([PR 886](https://github.com/input-output-hk/daedalus/pull/886))
- Example: