- Your code is going to be read many, many, many other developers. Always be conscious and thoughtful about making their jobs as easy as possible.
- Code is craft. Be proud of the code your write. If you aren't, get feedback and ideas from others!
- All files must be typed
- Use Typescript or flow
- Functions should be pure and maintain referential transparency
- Functions should not have unnecessary side effects
- Functions must be unit testable
- Leverage ES6 syntax to make writing functional JS easier
- If want to use
.forEach
, then check lodash for a more functional solution
- Never user
var
and avoid usinglet
unless strictly needed. - If you find yourself using
let
, check the following:- Can I use a
ternary operator
for assignment?const conditionalVariable = Boolean(otherVariable) ? 'Condition true' : 'Condition false';
- Can I extract this conditional logic into a helper function?
const multipleConditionsHelper = someValue => { switch (someValue) { case 'A': return 'Value A'; case 'B': return 'Value B'; default: return 'Value C'; } }; // further down in the file const otherFunction = otherValue => { const conditionalValue = multipleConditionsHelper(otherValue); };
- Can I use a
- Never do
someObject.foo = 'a'
// BAD const someObject = { bar: 'b' }; someObject.foo = 'a'; // GOOD const someObject = { bar: 'b' }; const newObject = { ...someObject, foo: 'a' }; // ALSO GOOD const someObject = { bar: 'b', foo: booleanValue ? 'a' : 'c', }; // ALSO GOOD const someObject = { bar: 'b', ...(booleanValue ? { foo: 'a' } : {}), };
- If you are desparate for mutation, use immer. Immer is also great for simplifying redux reducers!
-
Variables and functions must be
camelCased
or uppercasedSNAKE_CASE
for global constants -
Never use lowercase
snake_case
// GOOD const someFunction = () => {}; const someVariable = []; const SOME_CONSTANT = 1337; // BAD const bad_variable_name = {};
-
Variable names should signify its purpose
// GOOD const nameList = ['Devin', 'Joe', 'Josh']; // BAD const list = ['Devin', 'Joe', 'Josh'];
-
No magic numbers. Use constants for numbers.
// GOOD const POLL_RATE = 15 * 1000; const pollIntervalId = setInterval(() => { console.log('poll'); }, POLL_RATE); // BAD const pollIntervalId = setInterval(() => { console.log('poll'); }, 15 * 1000);
- If you find yourself duplicating logic extract, it into a utility function
- If you repeat logic twice it can be OK, but duplicating logic three times is
rarely
acceptable - If you create a helper function to simplify the logic in a function, do not define it within the scope of the function!
// GOOD const extractedComplicatedLogic = value => { // do stuff }; const importantFunction = currentValue => { const updatedValue = extractedComplicatedLogic(currentValue); // do more stuff }; // BAD const importantFunction = currentValue => { const extractedComplicatedLogic = value => { // do stuff }; const updatedValue = extractedComplicatedLogic(currentValue); // do more stuff };
- Always keep abstractions in mind.
- How can I make this a repeatable pattern?
- How can I make a helper to make this abstract away boilerplate?
- If you think of an abstraction idea and don't have time, then leave a
TODO
!
- Comments should enhance the readability of your code
- Always comment when understanding your code requires context beyond the scope of the file
- Explain the business logic that drove your decision
- Warn future developers that changing certain code will have unintended behaviors
- Emphasize the importance of a piece of code
- If you don't love your variable name, its probably a bad variable name
☹️ . But if you can't come up with a better one, leave a comment to explain the purpose of the variable! - Comment to help with future refactors
- It's way harder to comment too much than it is to comment too little, so when in doubt, leave a comment!
- Guards make functions more declarative and readable!
- If you use an
else
statement, then check to see if you can re-write using a guard.// BAD const someFunction = someValue => { if (someValue) { return 'A'; } else { return 'B'; } }; // GOOD // Much more readable! const someFunction = someValue => { if (!someValue) return 'B'; return 'A'; };
- Using
ES6
import
/export
makes it easier to unit test shared logic - Poorly maintained classes and closures lose the benefits of tree-shaking and dead code elimination
- Avoiding classes and closures forces you to think functionally and reduces reliance on an internal state
- There are use cases for classes and closures, but do not use unless strictly necessary.
// GOOD // util.js export const logger = value => console.log(`Logging ${value}`); // other-file.js import { logger } from './util.js'; // ------------------------------------ // GOOD // logger.js const logger = value => console.log(`Logging ${value}`); export default logger; // other-file.js import logger from './logger'; // ------------------------------------ // BAD // util.js class Utilities { logger = () => console.log(`Logging ${value}`); } export default Utilities; // other-file.js import Utilities from './util';
-
Use objects parameters for
import
-ed or shared functions -
Order doesn't matter! The beauty of using objects for parameters, means future developers do not have to worry parameter ordering!
// GOOD // helper.js const helperFunction = ({ foo, bar }) => return `${foo}${bar}`; // some-file.js import { helperFunction } from "../helpers"; // YAY! Other developers do not need to worry about order, only parameter names. const data = helperFunction({ bar: 'b', foo: 'a' }); // ------------------------------------------------------------------------ // BAD const badHelper = (foo, bar) => return `${foo}${bar}` // (BAD) Some developer accidentally messes up the order without knowing! const localFoo = 'a'; const localBar = 'b'; const data = badHelper(localBar, localFoo);
- Always use arrow functions for consistencey
// GOOD const someFunction = () => {}; // BAD function someFunction() {}
- Destructuring for object and array parameters/variables
- Destructure for variable assignment
// GOOD // Ex. Simple destructing parameters const functionWithObject = ({ foo, bar }) => {}; // Ex. Rename parameters const functionWithObject = ({ foo: renamedFoo, bar: renamedBar }) => {}; const assignmentWithDestructing = person => { const { firstName, lastName } = person; }; // BAD const assignmentWithoutDestructiong = person => { const firstName = person.firstName; const lastName = person.lastName; };
- Use the spread operation
...
for merging and cloning objects and manipulating arrays!const mergedObject = { ...oneObject, ...secondObject }; const clonedObject = { ...mergedObject }; const concatArrays = (arrayOne, arrayTwo) => [...arrayOne, ...arrayTwo]; const appendToArray = (array, item) => [...array, item]; const head = ([firstItem]) => firstItem; const [firstItem, secondItem] = veryLongArray;
- Avoid creating uneccesary function
- Most importantly for
react
component props - Use
_.flow
when you can to create a function that chains multiple functions
- Most importantly for
- Name branches
username/short-branch-description
- Always have good git hygiene
- Do not commit commented code
module.exports = {
env: {
browser: true,
'jest/globals': true,
},
globals: {
browser: true,
},
extends: [
'airbnb', // tons of good defaults (very strict)
'plugin:flowtype/recommended', // optional for flow!
'plugin:jest/recommended',
'prettier', // disable all rules that conflict with prettier
'prettier/react', // disable all rules that conflict with prettier
'prettier/flowtype', // disable all rules that conflict with prettier
],
// only include flow-type if we are using flow
plugins: ['flowtype', 'jest', 'react-hooks'],
rules: {
// START: Only if using flow
'react/prop-types': 'off',
'react/default-props-match-prop-types': 'off',
'react/no-multi-comp': 'off',
'react/require-default-props': 'off',
// require "// flow" at the top of all js files
'flowtype/require-valid-file-annotation': [
2,
'always',
{
annotationStyle: 'line',
},
],
// STOP: Only if using flow
// Hooks :)
'react-hooks/rules-of-hooks': 'error',
'no-plusplus': 'off',
'import/prefer-default-export': 'off',
// airbnb rules that are too picky
'react/destructuring-assignment': 'off',
'lines-between-class-members': 'off', // too strict
// we import types from jest
'jest/no-jest-import': 'off',
'no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
},
],
},
};
{
"trailingComma": "all",
"singleQuote": true,
"semicolons": true
}
- Always format on save!
- Assumes Sass
{
"plugins": ["stylelint-scss"],
"extends": "stylelint-config-recommended",
"rules": {
"at-rule-no-unknown": [
true,
{
"ignoreAtRules": ["extend", "if"]
}
],
"selector-pseudo-class-no-unknown": [
true,
{
"ignorePseudoClasses": ["global"]
}
]
}
}