Best practices for Javascript projects

Planning with development strategy is lowering the probability of delays or project failures. Examine some of the best practices that can help you deliver solid code with reduced effort and maintenance costs.

Know the language

The latest Javascript specification introduced useful syntax. It is recommended to use the default syntax over the previous unpure methods. Today you do not need Lodash, Underscore or Ramda most of the time.

Full Javascript course with examples, tips, and best practices

Javascript course — What is Javascript, where can I use it?

const, let | array methods map, filter, reduce | object spread, rest operator, destructuring

const, exceptionally let variable declaration

  • Will not pollute the upper or global scope, and const is signaling that the value will never change.

map, filter, reduce array methods

  • Do not need intermediate variables, can be chained.

Object and array destructuring, renaming variables and default values

  • Makes functions with an object as an argument simple and useful
arrow function, async-await, generators

Arrow functions — expression is a syntactically compact alternative to a regular function expression

  • const fn = () => {}; will prevent function name collisions
  • An arrow function does not have its own this
Arrow functions are alternatively called lambda expressions.

Async function — the declaration defines an asynchronous function

  • async code is written in synchronous style no more then chaining, and creating and returning of promises
const fn = async () => { return setTimeout(() => 42), 1000};
  • the async keyword is automatically transforming the output of the function into a new Promise
const fn = () => { 
return new Promise((resolve) => { resolve(42); }, 1000)
};

Code newer lies comments and documentation sometimes do

Clear Intention without comments

The code should be written like plain English communicating its intentions. The focus is to make code readable for the next customer your colleague developer.

Meaningful variable names

Correctly used variable names do not need comments and explanations that can get out of sync with the code rather quickly. The code is written and should be readable for humans.

The code is read more often than it is written.
do not use 🚨 data, val, str, x, y, z. use explicit variable names with the structure like valueInUnits
// 🚨 bad const time = 60; // 👍 good const timeInMinutes = 60;
// 🚨 bad const interest = 2.5; // 👍 good const yearlyInterestInPercents = 2.5;

Boolean values with the prefix “is” and “has”

Using variables with prefixes is and has will communicate clearly that the variable is a Boolean. The code is read more often than it is written.

use prefix is, has for boolean values

Prefer using named variables over comments

Explain conditions with variable names rather than comments

// 🚨 bad if (height > 100) {...} // 👍 good const hasRequiredHeight = heightInCm > 100;
if (hasRequiredHeight) {...}

Named variables can be reused and combined.

const isPositiveNumber = x > 0;
const isEvenNumber = x % 2;
const isOddNumber = !isEven;
const isPositiveEvenNumber = isPositive && isEven;

Readability over questionable performance improvements

Prefer array methods

It is recommended to use a functional approach without intermediate variables.

The base Javascript for cycle can be more performant in some browsers but the benefit can be measured only by iterating over millions of items. It is job of compiler and runtime to remove penalty of using new array methods.
// 🚨 bad let str = ""; for (var i = 0; i < 10; i++) {
str = `${str}${i}`;
} console.log(str);
// expected output: "0123456789"
// 👍 good console.log(
[...new Array(10).keys()].join("")
);
// expected output: "0123456789" // 🚨 bad let sum = 0;
for (var i = 0; i < 10; i++) {
sum = sum + i;
} console.log(sum);
// expected output: 45 // 👍 good console.log(
[...new Array(10).keys()]
.reduce((acc, key) => acc + key, 0)
);
// expected output: 45
We should ignore critics and switch to new syntax because it is shorter, more readable, and can be parallelized in the future.

trusting-mendel-p16mu - CodeSandbox

The problem when the function takes more parameters

The function should have a maximum of one required and one optional parameter. Most of the time use object as a parameter/argument.

Javascript course — Javascript the good parts frequently used in React

Creating and merging objects
It is not free to create an object on every call of the function. But the benefit of readability of the object argument is more significant than some preemptive micro-optimization.

Understand the benefits of testing

Tests are saving development time, and maintenance cost.

When tests are required developers are motivated to write smaller testable functions. Testing simple pure functions do not demand to mock large parts of the app.

Pure functions

A pure function is a function where the return value is only determined by its input values, without observable side effects.

Unit testing is better than debugging, whole app every time.

Testable Javascript -> Functional Programming -> Pure Functions

Separation of concerns

A Function should do one testable thing only. It is recommended to keep layers of the app separate from the other.

The function should inject constants and other functions using the object parameter with reasonable defaults that will open future overrides and ease testing.

UI rendering function should not have access to whole state or state management. Inject only necessary parts of the state and provide only necessary action creators/functions.

Component groups separation to allow testing

  • following a few simple rules will promote reusability and testability of components
  1. Atoms simple component with styles
  2. Molecules groups of simple components with styles
  3. Organisms components with minimal details for rendering can call functions to handle specific events. Can have style adjustments for specific business use cases

3*. Connector or state provider for Organisms provides or selects state for a component

4. Templates place components into a page layout that can be used in pages

5. Pages are specific instances of templates that show what a UI looks like with real representative content in place

Component groups

Static typing

Confidence added by static typing will compensate extended effort of writing types. Static typing with modern IDE is rapidly improving the developer experience with autocomplete and error highlighting.

Types can unveil the entire class of bugs that even testing can indicate.
Static typing reveals bugs on every keypress
  • Typescript is preferred by the library authors
  • Flow in some cases has some benefits for React projects

React and Typescript | Example

  • The goal is to create reusable components
  • A component can have other components in its default props
  • Default props used in render props pattern can be statically typed
type Props = {} & typeof defaultProps; type Children = (props: Props) => JSX.Element; export const Header = 
({children, ...props}: {children: Children} & Props) =>
children(props);
  • the default render function can be provided for styleguide
Header.defaultProps = {
...defaultProps,
children: ({Wrapper, Group}: Props) => (
<Wrapper>
<Group>Left</Group>
<Group>Center</Group>
<Group>Right</Group>
</Wrapper>
),
};

Component rendered with default children function

Header variant “space-between”
<Header />

Override component using props

Header variant “space-around”
const App = ({CustomHeaderWrapper}) => (
<Header Wrapper={CustomHeaderWrapper} />
); App.defaultProps = {
CustomHeaderWrapper: styled(Header.defaultProps.Wrapper)`
justify-content: space-around;
`,
};

Override component using props and provide render function

Header variant replacing children
<Header Wrapper={CustomHeaderWrapper}>
{({Wrapper, Group}) => (
<Wrapper>
<Group>Center</Group>
</Wrapper>
)}
</Header>

Type safety when you use an undefined prop

Type Error for “Missing” property of the component

Data management

selectors decouple shape of application state with shape of the component props

normalization and denormalization treat the state of the application as a relational database.

  • duplicities in application state produce bugs when updates do not address all the duplicities
  • loading and storing nested entities significantly enlarge rest payloads and processing
  • prefer the use projects like normalizr and normalization of nested JSON according to a schema

Invest in tooling that can save time

An example can be found in https://github.com/ableneo/modules and eslint config in the eslint-config-ableneo.
Config installation guide.

precommit should

  • pretty print
  • lint the code, type checking
  • run all the tests related to changed files in git

prepush

  • lint the code, type checking
  • run all the unit test

CD/CI

  • try to merge
  • run prepush checks again
  • start build
prettier, eslint, babel, typescript

Code generators

There are two good generator libraries plopjs and hygen. A short hygen tutorial is available in an article with an example.

How to use the hygen code generator

Another example of hygen is initializer hygen-eslint-config-ableneo for our eslint-config-ableneo

Style guides

Boost developer experience with a styleguide. Developers and designers can iterate faster on generalized components.

List of living style guide projects worth mentioning

Example of public documentation/styleguide using docz. https://ableneo.github.io/modules/

Conclusion

The main goal is to enable fast and reliable development supporting the business needs of the customer. Use tools that fit the project, developers and designers. Try to remove repeatable tasks by automatization from the workflow.

👏Clap, 👂follow for more awesome 💟#Javascript and ⚛️#automatization content.


Best practices for Javascript projects was originally published in ableneo Technology on Medium, where people are continuing the conversation by highlighting and responding to this story.