SlideShare a Scribd company logo
Clean codeClean codeAdapted in JavaScript
Clean code in JavaScript
AgendaAgenda
Variables
Functions
Classes
Miscellaneous
VariablesVariables
There are only two hard things inThere are only two hard things in
Computer Science: cache invalidationComputer Science: cache invalidation
and naming things.and naming things.
There are only two hard things inThere are only two hard things in
Computer Science: cache invalidationComputer Science: cache invalidation
and naming things.and naming things.
“ Phil Karlton
Use meaningful and pronounceable variable namesUse meaningful and pronounceable variable names
const yyyymmdstr = moment().format('YYYY/MM/DD');
const yearMonthDay = moment().format('YYYY/MM/DD');
Use the same vocabulary for the same type of variableUse the same vocabulary for the same type of variable
getUserInfo();
getClientData();
getCustomerRecord();
getUser();
Use searchable namesUse searchable names
// What the heck is 525600 for?
for (let i = 0; i < 525600; i++) {
runCronJob();
}
// Declare them as capitalized `const` globals.
const MINUTES_IN_A_YEAR = 525600;
for (let i = 0; i < MINUTES_IN_A_YEAR; i++) {
runCronJob();
}
Keep convention in mindKeep convention in mind
class AnimalSoSweet {} // UpperCamelCase
let anAnimal = new AnimalSoSweet(); // LowerCamlCase
function sendARequest(requestToSend){};// LowerCamlCase
const NB_MS_IN_HOUR = 3600; // SCREAMING_SNAKE_CASE
Use explanatory variablesUse explanatory variables
const cityStateRegex = /^(.+)[,s]+(.+?)s*(d{5})?$/;
saveCityState(
cityStateRegex.match(cityStateRegex)[1],
cityStateRegex.match(cityStateRegex)[2]
);
const cityStateRegex = /^(.+)[,s]+(.+?)s*(d{5})?$/;
const match = cityStateRegex.match(cityStateRegex)
const city = match[1];
const state = match[2];
saveCityState(city, state);
Avoid Mental MappingAvoid Mental Mapping
const locations = ['Austin', 'New York', 'San Francisco'];
locations.forEach((l) => {
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
// Wait, what is `l` for again?
dispatch(l);
});
const locations = ['Austin', 'New York', 'San Francisco'];
locations.forEach((location) => {
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
dispatch(location);
});
Don't add unneeded contextDon't add unneeded context
const Car = {
carMake: 'Honda',
carModel: 'Accord',
carColor: 'Blue'
};
function paintCar(car) {
car.carColor = 'Red';
}
const Car = {
make: 'Honda',
model: 'Accord',
color: 'Blue'
};
function paintCar(car) {
car.color = 'Red';
}
Short-circuiting is cleaner than conditionalsShort-circuiting is cleaner than conditionals
function createMicrobrewery(name) {
let breweryName;
if (name) {
breweryName = name;
} else {
breweryName = 'Hipster Brew Co.';
}
}
function createMicrobrewery(name) {
const breweryName = name || 'Hipster Brew Co.'
}
FunctionsFunctions
Function arguments (2 or fewer ideally)Function arguments (2 or fewer ideally)
function createMenu(title, body, buttonText, cancellable) {
// ...
}
const menuConfig = {
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
}
function createMenu(menuConfig) {
// ...
}
Functions should do one thing (Small !)Functions should do one thing (Small !)
function emailClients(clients) {
clients.forEach(client => {
const clientRecord = database.lookup(client);
if (clientRecord.isActive()) {
email(client);
}
});
}
function emailClients(clients) {
clients
.filter(isClientActive)
.forEach(email);
}
function isClientActive(client) {
const clientRecord = database.lookup(client);
return clientRecord.isActive();
}
Don't use flags as function parametersDon't use flags as function parameters
function createFile(name, temp) {
if (temp) {
fs.create('./temp/' + name);
} else {
fs.create(name);
}
}
function createFile(name) {
fs.create(name);
}
function createTempFile(name) {
createFile('./temp/' + name);
}
Function names should say what they doFunction names should say what they do
function dateAdd(date, month) {
// ...
}
const date = new Date();
/* It's hard to to tell from the
function name what is added
*/
dateAdd(date, 1);
function dateAddMonth(date, month) {
// ...
}
const date = new Date();
dateAddMonth(date, 1);
Function should not have side effectFunction should not have side effect
let name = 'Ryan McDermott';
function splitIntoFirstAndLastName() {
name = name.split(' ');
}
splitIntoFirstAndLastName();
function splitIntoFirstAndLastName(name) {
return name.split(' ');
}
const name = 'Ryan McDermott'
const newName = splitIntoFirstAndLastName(name);
Use default arguments instead of short circuitingUse default arguments instead of short circuiting
function writeForumComment(subject, body) {
subject = subject || 'No Subject';
body = body || 'No text';
}
function writeForumComment(
subject = 'No subject',
body = 'No text') {
// ...
}
Encapsulate conditionalsEncapsulate conditionals
if (fsm.state === 'fetching' && isEmpty(listNode)) {
// ...
}
function shouldShowSpinner(fsm, listNode) {
return fsm.state === 'fetching' && isEmpty(listNode);
}
if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
// ...
}
Sort correctly callers and calleeSort correctly callers and callee
function doVerySpecificThing(){
//...
}
function doSpecificThing(){
//...
doVerySpecificThing();
}
function doThing(){
//...
doSpecificThing();
}
function doThing(){
//...
doSpecificThing();
}
function doSpecificThing(){
//...
doVerySpecificThing();
}
function doVerySpecificThing(){
//...
}
Prefer functional programming over imperativePrefer functional programming over imperative
const programmerOutput = [
{ name: 'Uncle Bobby', linesOfCode: 500 },
{ name: 'Suzie Q', linesOfCode: 1500 }
//....
];
let totalOutput = 0;
for (let i = 0; i < programmerOutput.length; i++) {
totalOutput += programmerOutput[i].linesOfCode;
}
const programmerOutput = [
{ name: 'Uncle Bobby', linesOfCode: 500 },
{ name: 'Suzie Q', linesOfCode: 1500 }
//....
];
const totalOutput = programmerOutput
.map((programmer) => programmer.linesOfCode)
.reduce((acc, linesOfCode) => acc + linesOfCode, 0);
Prefer polymorphism over lot of conditionalsPrefer polymorphism over lot of conditionals
class Airplane {
// ...
getCruisingAltitude() {
switch (this.type) {
case '777':
return this.getMaxAltitude() - this.getPassengerCount();
case 'Air Force One':
return this.getMaxAltitude();
case 'Cessna':
return this.getMaxAltitude() - this.getFuelExpenditure();
}
}
}
class Airplane {}
class Boeing777 extends Airplane {
getCruisingAltitude() {
return this.getMaxAltitude() - this.getPassengerCount();
}
}
class AirForceOne extends Airplane {
getCruisingAltitude() {
return this.getMaxAltitude();
}
}
Never everNever ever lets dead or commented code !!!lets dead or commented code !!!
// function oldRequestModule(url) {
// ...
// }
function newRequestModule(url) {
// ...
}
const req = newRequestModule;
inventoryTracker('apples', req, 'www.inventory-awesome.io');
function newRequestModule(url) {
// ...
}
const req = newRequestModule;
inventoryTracker('apples', req, 'www.inventory-awesome.io');
ClassesClasses
SSingle responsibilityingle responsibility
OOpen-closedpen-closed
LLiskov substitutioniskov substitution
IInterface segregationnterface segregation
DDependency injectionependency injection
SOLIDSOLID principlesprinciples
en.wikipedia.org/wiki/SOLID_(object-oriented_design)
Single Responsibility Principle (Single Responsibility Principle (SSRP)RP)
class UserService {
saveUser(user){
eventEmmitter.dispatch(ADD_USER, user);
return db.exec('INSERT VALUES ...', user);
}
}
class UserResource {
//...
save(user){
return db.exec('INSERT VALUES ...', user);
}
}
class UserService {
//...
saveUser(user){
eventEmmitter.dispatch(ADD_USER, user);
return userRessource.save(user)
}
}
"There should never be more than one reason for a class to change"
Open/Closed Principle (Open/Closed Principle (OOCP)CP)
class ExpenseController {
constructor() {}
addNewExpense(expense) {
if (this.validateExpense(expense)){
expenseService.save(expense);
}
}
validateExpense (expense){
return !!expense.description;
}
}
class UserAuth {
constructor(user) {
this.user = user;
}
verifyCredentials() {
// ...
}
}
class ExpenseController {
constructor(validators) {this.validators=validators;}
addNewExpense(expense) {
if (this.validateExpense(expense)){
expenseService.save(expense);
}
}
validateExpense (expense){
return this.validators.every(validator => {
return validator.isValid(expense));
});
}
}
"open for extension, but closed for modification"
Liskov substitution principle (Liskov substitution principle (LLSP)SP)
class Rectangle {
//...
setWidth(width) {
this.width = width;
}
setHeight(height) {
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
class Square extends Rectangle {
// ...
setWidth(width) {
this.width = width;
this.height = width;
}
setHeight(height) {
this.width = height;
this.height = height;
}
}
function renderLargeRectangles(rectangles) {
rectangles.forEach((rectangle) => {
rectangle.setWidth(4);
rectangle.setHeight(5);
const area = rectangle.getArea();//Will return 25 for Square. Should be 20.
rectangle.render(area);
});
}
const rectangles = [new Rectangle(), new Rectangle(), new Square()];
renderLargeRectangles(rectangles);
Liskov substitution principle (Liskov substitution principle (LLSP)SP)
class Shape {
setColor(color) {
// ...
}
render(area) {
// ...
}
}
class Rectangle extends Shape {
constructor() {
super();
this.width = 0;
this.height = 0;
}
setWidth(width) {
this.width = width;
}
setHeight(height) {
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
class Square extends Shape {
constructor() {
super();
this.length = 0;
}
setLength(length) {
this.length = length;
}
getArea() {
return this.length * this.length;
}
}
function renderLargeShapes(shapes) {
shapes.forEach((shape) => {
switch (shape.constructor.name) {
case 'Square':
shape.setLength(5);
break;
case 'Rectangle':
shape.setWidth(4);
shape.setHeight(5);
}
const area = shape.getArea();
shape.render(area);
});
}
Interface segregation principle (Interface segregation principle (IISP)SP)
class ACar {
run(){
throw new Error('SubType of car should implement run');
}
fillFuel(){
throw new Error('SubType of car should implement fillFuel');
}
}
class MyOldCar extends ACar{
run(){
// run ...
}
fillFuel(){
// handle fillFuel
}
}
class MyElectricCar extends ACar{
run(){
// run ...
}
fillFuel(){
// Not necessary
}
}
"Clients should not be forced to depend upon interfaces that they do not use."
Interface segregation principle (Interface segregation principle (IISP)SP)
class ACar {
run(){
throw new Error('SubType of car should implement run');
}
}
const FuelCar = (Superclass) => class extends Superclass {
fillFuel() {
throw new Error('SubType of FuelCar should implement fillFuel');
}
}
class MyOldCar extends FuelCar(ACar){
run(){
// run ...
}
fillFuel(){
// handle fillFuel
}
}
const ElectricCar = (Superclass) => class extends Superclass {
reload() {
throw new Error('SubType of ElectricCar should implement reload');
}
}
class MyElectricCar extends ElectricCar(ACar){
run(){
// run ...
}
reload(){
// handle reload
}
}
"Clients should not be forced to depend upon interfaces that they do not use."
Dependency Inversion Principle (Dependency Inversion Principle (DDIP)IP)
"1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
2. Abstractions should not depend upon details. Details should depend on abstractions."
class UserController {
constructor(){
this.userService = new UserService();
}
save(user){
this.userService.save(user);
}
}
class UserController {
constructor(userService){
this.userService = userService;
}
save(user){
this.userService.save(user);
}
}
Use method chainingUse method chaining
"1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
2. Abstractions should not depend upon details. Details should depend on abstractions."
class User {
constructor(name, age){
this.name = name;
this.age = age;
}
setName(name){
this.name = name;
}
setAge(age){
this.age = age;
}
}
const user = new User();
user.setName('Mathieu');
user.setAge('28');
class User {
constructor(name, age){
this.name = name;
this.age = age;
}
setName(name){
this.name = name;
return this;
}
setAge(age){
this.age = age;
return this;
}
}
const user = new User()
.setName('Mathieu')
.setAge('28');
MiscellaneousMiscellaneous   
Only comment things that have business logic complexity.Only comment things that have business logic complexity.
function hashIt(data) {
// The hash
let hash = 0;
// Length of string
const length = data.length;
// Loop through every character in data
for (let i = 0; i < length; i++) {
// Get character code.
const char = data.charCodeAt(i);
// Make the hash
hash = ((hash << 5) - hash) + char;
// Convert to 32-bit integer
hash &= hash;
}
}
"Comments are an apology, not a requirement. Good code mostly documents itself."
function hashIt(data) {
let hash = 0;
const length = data.length;
for (let i = 0; i < length; i++) {
const char = data.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
// Convert to 32-bit integer
hash &= hash;
}
}
Bad commentsBad comments
/**
* 2016-12-20: Removed monads, didn't understand them (RM)
* 2016-10-01: Improved using special monads (JP)
* 2016-02-03: Removed type-checking (LI)
* 2015-03-14: Added combine with type-checking (JR)
*/
////////////////////////////////////////////////////////////////////////////////
// Scope Model Instantiation
////////////////////////////////////////////////////////////////////////////////
$scope.model = {
menu: 'foo',
nav: 'bar'
};
while (toto < MAX_VALUES){
if (test > MIN_VALUES){
} // if
} // while
FollowFollow
the cleanthe clean
code rulescode rules
slides.com/mbreton/clean-code-javascriptslides.com/mbreton/clean-code-javascript
github.com/ryanmcdermott/clean-code-javascriptgithub.com/ryanmcdermott/clean-code-javascript

More Related Content

What's hot (20)

PDF
Introduction to React JS
Bethmi Gunasekara
 
PDF
ES6 presentation
ritika1
 
PDF
The New JavaScript: ES6
Rob Eisenberg
 
PDF
Workshop 21: React Router
Visual Engineering
 
PPTX
React state
Ducat
 
PDF
ES2015 / ES6: Basics of modern Javascript
Wojciech Dzikowski
 
PPTX
React & Redux JS
Hamed Farag
 
PDF
Understanding react hooks
Samundra khatri
 
PDF
Basics of React Hooks.pptx.pdf
Knoldus Inc.
 
PPTX
React hooks
Assaf Gannon
 
PDF
JavaScript Programming
Sehwan Noh
 
PPTX
React js
Oswald Campesato
 
PPTX
SOLID principles
Jonathan Holloway
 
PPTX
[Final] ReactJS presentation
洪 鹏发
 
PDF
Clean code
Alvaro García Loaisa
 
PPTX
React hooks
Ramy ElBasyouni
 
PDF
React
중운 박
 
PDF
React JS - Introduction
Sergey Romaneko
 
PDF
JavaScript - Chapter 9 - TypeConversion and Regular Expressions
WebStackAcademy
 
PPTX
Java 11 to 17 : What's new !?
Jérôme Tamborini
 
Introduction to React JS
Bethmi Gunasekara
 
ES6 presentation
ritika1
 
The New JavaScript: ES6
Rob Eisenberg
 
Workshop 21: React Router
Visual Engineering
 
React state
Ducat
 
ES2015 / ES6: Basics of modern Javascript
Wojciech Dzikowski
 
React & Redux JS
Hamed Farag
 
Understanding react hooks
Samundra khatri
 
Basics of React Hooks.pptx.pdf
Knoldus Inc.
 
React hooks
Assaf Gannon
 
JavaScript Programming
Sehwan Noh
 
SOLID principles
Jonathan Holloway
 
[Final] ReactJS presentation
洪 鹏发
 
React hooks
Ramy ElBasyouni
 
React
중운 박
 
React JS - Introduction
Sergey Romaneko
 
JavaScript - Chapter 9 - TypeConversion and Regular Expressions
WebStackAcademy
 
Java 11 to 17 : What's new !?
Jérôme Tamborini
 

Similar to Clean code in JavaScript (20)

PDF
Clean code
Arturo Herrero
 
PPTX
ES6 Overview
Bruno Scopelliti
 
PPTX
Pro typescript.ch03.Object Orientation in TypeScript
Seok-joon Yun
 
PDF
Hello Swift Final 5/5 - Structures and Classes
Cody Yun
 
PPTX
Architecting JavaScript Code
Suresh Balla
 
PDF
Say It With Javascript
Giovanni Scerra ☃
 
PPT
Wakanday JS201 Best Practices
Juergen Fesslmeier
 
KEY
Symfony2 Building on Alpha / Beta technology
Daniel Knell
 
PDF
JavaScript Core
François Sarradin
 
PDF
JavaScript - Agora nervoso
Luis Vendrame
 
PDF
Applying Real-time SQL Changes in your Hazelcast Data Grid
Hazelcast
 
KEY
Object-Oriented Javascript
kvangork
 
KEY
Object-Oriented JavaScript
kvangork
 
PPTX
Thinking Functionally with JavaScript
Luis Atencio
 
PPTX
AST - the only true tool for building JavaScript
Ingvar Stepanyan
 
PDF
JavaScript and UI Architecture Best Practices
Siarhei Barysiuk
 
PDF
Building Lithium Apps
Nate Abele
 
PDF
Doctrine For Beginners
Jonathan Wage
 
PDF
Dependency Management with RequireJS
Aaronius
 
PDF
From android/ java to swift (2)
allanh0526
 
Clean code
Arturo Herrero
 
ES6 Overview
Bruno Scopelliti
 
Pro typescript.ch03.Object Orientation in TypeScript
Seok-joon Yun
 
Hello Swift Final 5/5 - Structures and Classes
Cody Yun
 
Architecting JavaScript Code
Suresh Balla
 
Say It With Javascript
Giovanni Scerra ☃
 
Wakanday JS201 Best Practices
Juergen Fesslmeier
 
Symfony2 Building on Alpha / Beta technology
Daniel Knell
 
JavaScript Core
François Sarradin
 
JavaScript - Agora nervoso
Luis Vendrame
 
Applying Real-time SQL Changes in your Hazelcast Data Grid
Hazelcast
 
Object-Oriented Javascript
kvangork
 
Object-Oriented JavaScript
kvangork
 
Thinking Functionally with JavaScript
Luis Atencio
 
AST - the only true tool for building JavaScript
Ingvar Stepanyan
 
JavaScript and UI Architecture Best Practices
Siarhei Barysiuk
 
Building Lithium Apps
Nate Abele
 
Doctrine For Beginners
Jonathan Wage
 
Dependency Management with RequireJS
Aaronius
 
From android/ java to swift (2)
allanh0526
 
Ad

More from Mathieu Breton (8)

PDF
TDD in Javascript
Mathieu Breton
 
PDF
Meet VueJs
Mathieu Breton
 
PDF
FalcorJS
Mathieu Breton
 
PDF
BDD in Javascript
Mathieu Breton
 
PDF
NodeJS Spring style Inversifyjs
Mathieu Breton
 
PDF
Rollup.js
Mathieu Breton
 
PDF
Présentation de Dart
Mathieu Breton
 
PDF
JavaScript the-next-big...bytecode
Mathieu Breton
 
TDD in Javascript
Mathieu Breton
 
Meet VueJs
Mathieu Breton
 
FalcorJS
Mathieu Breton
 
BDD in Javascript
Mathieu Breton
 
NodeJS Spring style Inversifyjs
Mathieu Breton
 
Rollup.js
Mathieu Breton
 
Présentation de Dart
Mathieu Breton
 
JavaScript the-next-big...bytecode
Mathieu Breton
 
Ad

Recently uploaded (20)

PPTX
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
PDF
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
PPTX
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
PPTX
From Sci-Fi to Reality: Exploring AI Evolution
Svetlana Meissner
 
PPTX
WooCommerce Workshop: Bring Your Laptop
Laura Hartwig
 
PPTX
COMPARISON OF RASTER ANALYSIS TOOLS OF QGIS AND ARCGIS
Sharanya Sarkar
 
PDF
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
PDF
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
PDF
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 
PDF
Chris Elwell Woburn, MA - Passionate About IT Innovation
Chris Elwell Woburn, MA
 
PPTX
Webinar: Introduction to LF Energy EVerest
DanBrown980551
 
PDF
July Patch Tuesday
Ivanti
 
PDF
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
PDF
Exolore The Essential AI Tools in 2025.pdf
Srinivasan M
 
PDF
Bitcoin for Millennials podcast with Bram, Power Laws of Bitcoin
Stephen Perrenod
 
PDF
New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
PDF
Reverse Engineering of Security Products: Developing an Advanced Microsoft De...
nwbxhhcyjv
 
PDF
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
PPTX
OpenID AuthZEN - Analyst Briefing July 2025
David Brossard
 
PDF
NewMind AI - Journal 100 Insights After The 100th Issue
NewMind AI
 
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
From Sci-Fi to Reality: Exploring AI Evolution
Svetlana Meissner
 
WooCommerce Workshop: Bring Your Laptop
Laura Hartwig
 
COMPARISON OF RASTER ANALYSIS TOOLS OF QGIS AND ARCGIS
Sharanya Sarkar
 
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 
Chris Elwell Woburn, MA - Passionate About IT Innovation
Chris Elwell Woburn, MA
 
Webinar: Introduction to LF Energy EVerest
DanBrown980551
 
July Patch Tuesday
Ivanti
 
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
Exolore The Essential AI Tools in 2025.pdf
Srinivasan M
 
Bitcoin for Millennials podcast with Bram, Power Laws of Bitcoin
Stephen Perrenod
 
New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
Reverse Engineering of Security Products: Developing an Advanced Microsoft De...
nwbxhhcyjv
 
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
OpenID AuthZEN - Analyst Briefing July 2025
David Brossard
 
NewMind AI - Journal 100 Insights After The 100th Issue
NewMind AI
 

Clean code in JavaScript

  • 5. There are only two hard things inThere are only two hard things in Computer Science: cache invalidationComputer Science: cache invalidation and naming things.and naming things. There are only two hard things inThere are only two hard things in Computer Science: cache invalidationComputer Science: cache invalidation and naming things.and naming things. “ Phil Karlton
  • 6. Use meaningful and pronounceable variable namesUse meaningful and pronounceable variable names const yyyymmdstr = moment().format('YYYY/MM/DD'); const yearMonthDay = moment().format('YYYY/MM/DD');
  • 7. Use the same vocabulary for the same type of variableUse the same vocabulary for the same type of variable getUserInfo(); getClientData(); getCustomerRecord(); getUser();
  • 8. Use searchable namesUse searchable names // What the heck is 525600 for? for (let i = 0; i < 525600; i++) { runCronJob(); } // Declare them as capitalized `const` globals. const MINUTES_IN_A_YEAR = 525600; for (let i = 0; i < MINUTES_IN_A_YEAR; i++) { runCronJob(); }
  • 9. Keep convention in mindKeep convention in mind class AnimalSoSweet {} // UpperCamelCase let anAnimal = new AnimalSoSweet(); // LowerCamlCase function sendARequest(requestToSend){};// LowerCamlCase const NB_MS_IN_HOUR = 3600; // SCREAMING_SNAKE_CASE
  • 10. Use explanatory variablesUse explanatory variables const cityStateRegex = /^(.+)[,s]+(.+?)s*(d{5})?$/; saveCityState( cityStateRegex.match(cityStateRegex)[1], cityStateRegex.match(cityStateRegex)[2] ); const cityStateRegex = /^(.+)[,s]+(.+?)s*(d{5})?$/; const match = cityStateRegex.match(cityStateRegex) const city = match[1]; const state = match[2]; saveCityState(city, state);
  • 11. Avoid Mental MappingAvoid Mental Mapping const locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((l) => { doStuff(); doSomeOtherStuff(); // ... // ... // ... // Wait, what is `l` for again? dispatch(l); }); const locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((location) => { doStuff(); doSomeOtherStuff(); // ... // ... // ... dispatch(location); });
  • 12. Don't add unneeded contextDon't add unneeded context const Car = { carMake: 'Honda', carModel: 'Accord', carColor: 'Blue' }; function paintCar(car) { car.carColor = 'Red'; } const Car = { make: 'Honda', model: 'Accord', color: 'Blue' }; function paintCar(car) { car.color = 'Red'; }
  • 13. Short-circuiting is cleaner than conditionalsShort-circuiting is cleaner than conditionals function createMicrobrewery(name) { let breweryName; if (name) { breweryName = name; } else { breweryName = 'Hipster Brew Co.'; } } function createMicrobrewery(name) { const breweryName = name || 'Hipster Brew Co.' }
  • 15. Function arguments (2 or fewer ideally)Function arguments (2 or fewer ideally) function createMenu(title, body, buttonText, cancellable) { // ... } const menuConfig = { title: 'Foo', body: 'Bar', buttonText: 'Baz', cancellable: true } function createMenu(menuConfig) { // ... }
  • 16. Functions should do one thing (Small !)Functions should do one thing (Small !) function emailClients(clients) { clients.forEach(client => { const clientRecord = database.lookup(client); if (clientRecord.isActive()) { email(client); } }); } function emailClients(clients) { clients .filter(isClientActive) .forEach(email); } function isClientActive(client) { const clientRecord = database.lookup(client); return clientRecord.isActive(); }
  • 17. Don't use flags as function parametersDon't use flags as function parameters function createFile(name, temp) { if (temp) { fs.create('./temp/' + name); } else { fs.create(name); } } function createFile(name) { fs.create(name); } function createTempFile(name) { createFile('./temp/' + name); }
  • 18. Function names should say what they doFunction names should say what they do function dateAdd(date, month) { // ... } const date = new Date(); /* It's hard to to tell from the function name what is added */ dateAdd(date, 1); function dateAddMonth(date, month) { // ... } const date = new Date(); dateAddMonth(date, 1);
  • 19. Function should not have side effectFunction should not have side effect let name = 'Ryan McDermott'; function splitIntoFirstAndLastName() { name = name.split(' '); } splitIntoFirstAndLastName(); function splitIntoFirstAndLastName(name) { return name.split(' '); } const name = 'Ryan McDermott' const newName = splitIntoFirstAndLastName(name);
  • 20. Use default arguments instead of short circuitingUse default arguments instead of short circuiting function writeForumComment(subject, body) { subject = subject || 'No Subject'; body = body || 'No text'; } function writeForumComment( subject = 'No subject', body = 'No text') { // ... }
  • 21. Encapsulate conditionalsEncapsulate conditionals if (fsm.state === 'fetching' && isEmpty(listNode)) { // ... } function shouldShowSpinner(fsm, listNode) { return fsm.state === 'fetching' && isEmpty(listNode); } if (shouldShowSpinner(fsmInstance, listNodeInstance)) { // ... }
  • 22. Sort correctly callers and calleeSort correctly callers and callee function doVerySpecificThing(){ //... } function doSpecificThing(){ //... doVerySpecificThing(); } function doThing(){ //... doSpecificThing(); } function doThing(){ //... doSpecificThing(); } function doSpecificThing(){ //... doVerySpecificThing(); } function doVerySpecificThing(){ //... }
  • 23. Prefer functional programming over imperativePrefer functional programming over imperative const programmerOutput = [ { name: 'Uncle Bobby', linesOfCode: 500 }, { name: 'Suzie Q', linesOfCode: 1500 } //.... ]; let totalOutput = 0; for (let i = 0; i < programmerOutput.length; i++) { totalOutput += programmerOutput[i].linesOfCode; } const programmerOutput = [ { name: 'Uncle Bobby', linesOfCode: 500 }, { name: 'Suzie Q', linesOfCode: 1500 } //.... ]; const totalOutput = programmerOutput .map((programmer) => programmer.linesOfCode) .reduce((acc, linesOfCode) => acc + linesOfCode, 0);
  • 24. Prefer polymorphism over lot of conditionalsPrefer polymorphism over lot of conditionals class Airplane { // ... getCruisingAltitude() { switch (this.type) { case '777': return this.getMaxAltitude() - this.getPassengerCount(); case 'Air Force One': return this.getMaxAltitude(); case 'Cessna': return this.getMaxAltitude() - this.getFuelExpenditure(); } } } class Airplane {} class Boeing777 extends Airplane { getCruisingAltitude() { return this.getMaxAltitude() - this.getPassengerCount(); } } class AirForceOne extends Airplane { getCruisingAltitude() { return this.getMaxAltitude(); } }
  • 25. Never everNever ever lets dead or commented code !!!lets dead or commented code !!! // function oldRequestModule(url) { // ... // } function newRequestModule(url) { // ... } const req = newRequestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io'); function newRequestModule(url) { // ... } const req = newRequestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io');
  • 27. SSingle responsibilityingle responsibility OOpen-closedpen-closed LLiskov substitutioniskov substitution IInterface segregationnterface segregation DDependency injectionependency injection SOLIDSOLID principlesprinciples en.wikipedia.org/wiki/SOLID_(object-oriented_design)
  • 28. Single Responsibility Principle (Single Responsibility Principle (SSRP)RP) class UserService { saveUser(user){ eventEmmitter.dispatch(ADD_USER, user); return db.exec('INSERT VALUES ...', user); } } class UserResource { //... save(user){ return db.exec('INSERT VALUES ...', user); } } class UserService { //... saveUser(user){ eventEmmitter.dispatch(ADD_USER, user); return userRessource.save(user) } } "There should never be more than one reason for a class to change"
  • 29. Open/Closed Principle (Open/Closed Principle (OOCP)CP) class ExpenseController { constructor() {} addNewExpense(expense) { if (this.validateExpense(expense)){ expenseService.save(expense); } } validateExpense (expense){ return !!expense.description; } } class UserAuth { constructor(user) { this.user = user; } verifyCredentials() { // ... } } class ExpenseController { constructor(validators) {this.validators=validators;} addNewExpense(expense) { if (this.validateExpense(expense)){ expenseService.save(expense); } } validateExpense (expense){ return this.validators.every(validator => { return validator.isValid(expense)); }); } } "open for extension, but closed for modification"
  • 30. Liskov substitution principle (Liskov substitution principle (LLSP)SP) class Rectangle { //... setWidth(width) { this.width = width; } setHeight(height) { this.height = height; } getArea() { return this.width * this.height; } } class Square extends Rectangle { // ... setWidth(width) { this.width = width; this.height = width; } setHeight(height) { this.width = height; this.height = height; } } function renderLargeRectangles(rectangles) { rectangles.forEach((rectangle) => { rectangle.setWidth(4); rectangle.setHeight(5); const area = rectangle.getArea();//Will return 25 for Square. Should be 20. rectangle.render(area); }); } const rectangles = [new Rectangle(), new Rectangle(), new Square()]; renderLargeRectangles(rectangles);
  • 31. Liskov substitution principle (Liskov substitution principle (LLSP)SP) class Shape { setColor(color) { // ... } render(area) { // ... } } class Rectangle extends Shape { constructor() { super(); this.width = 0; this.height = 0; } setWidth(width) { this.width = width; } setHeight(height) { this.height = height; } getArea() { return this.width * this.height; } } class Square extends Shape { constructor() { super(); this.length = 0; } setLength(length) { this.length = length; } getArea() { return this.length * this.length; } } function renderLargeShapes(shapes) { shapes.forEach((shape) => { switch (shape.constructor.name) { case 'Square': shape.setLength(5); break; case 'Rectangle': shape.setWidth(4); shape.setHeight(5); } const area = shape.getArea(); shape.render(area); }); }
  • 32. Interface segregation principle (Interface segregation principle (IISP)SP) class ACar { run(){ throw new Error('SubType of car should implement run'); } fillFuel(){ throw new Error('SubType of car should implement fillFuel'); } } class MyOldCar extends ACar{ run(){ // run ... } fillFuel(){ // handle fillFuel } } class MyElectricCar extends ACar{ run(){ // run ... } fillFuel(){ // Not necessary } } "Clients should not be forced to depend upon interfaces that they do not use."
  • 33. Interface segregation principle (Interface segregation principle (IISP)SP) class ACar { run(){ throw new Error('SubType of car should implement run'); } } const FuelCar = (Superclass) => class extends Superclass { fillFuel() { throw new Error('SubType of FuelCar should implement fillFuel'); } } class MyOldCar extends FuelCar(ACar){ run(){ // run ... } fillFuel(){ // handle fillFuel } } const ElectricCar = (Superclass) => class extends Superclass { reload() { throw new Error('SubType of ElectricCar should implement reload'); } } class MyElectricCar extends ElectricCar(ACar){ run(){ // run ... } reload(){ // handle reload } } "Clients should not be forced to depend upon interfaces that they do not use."
  • 34. Dependency Inversion Principle (Dependency Inversion Principle (DDIP)IP) "1. High-level modules should not depend on low-level modules. Both should depend on abstractions. 2. Abstractions should not depend upon details. Details should depend on abstractions." class UserController { constructor(){ this.userService = new UserService(); } save(user){ this.userService.save(user); } } class UserController { constructor(userService){ this.userService = userService; } save(user){ this.userService.save(user); } }
  • 35. Use method chainingUse method chaining "1. High-level modules should not depend on low-level modules. Both should depend on abstractions. 2. Abstractions should not depend upon details. Details should depend on abstractions." class User { constructor(name, age){ this.name = name; this.age = age; } setName(name){ this.name = name; } setAge(age){ this.age = age; } } const user = new User(); user.setName('Mathieu'); user.setAge('28'); class User { constructor(name, age){ this.name = name; this.age = age; } setName(name){ this.name = name; return this; } setAge(age){ this.age = age; return this; } } const user = new User() .setName('Mathieu') .setAge('28');
  • 37. Only comment things that have business logic complexity.Only comment things that have business logic complexity. function hashIt(data) { // The hash let hash = 0; // Length of string const length = data.length; // Loop through every character in data for (let i = 0; i < length; i++) { // Get character code. const char = data.charCodeAt(i); // Make the hash hash = ((hash << 5) - hash) + char; // Convert to 32-bit integer hash &= hash; } } "Comments are an apology, not a requirement. Good code mostly documents itself." function hashIt(data) { let hash = 0; const length = data.length; for (let i = 0; i < length; i++) { const char = data.charCodeAt(i); hash = ((hash << 5) - hash) + char; // Convert to 32-bit integer hash &= hash; } }
  • 38. Bad commentsBad comments /** * 2016-12-20: Removed monads, didn't understand them (RM) * 2016-10-01: Improved using special monads (JP) * 2016-02-03: Removed type-checking (LI) * 2015-03-14: Added combine with type-checking (JR) */ //////////////////////////////////////////////////////////////////////////////// // Scope Model Instantiation //////////////////////////////////////////////////////////////////////////////// $scope.model = { menu: 'foo', nav: 'bar' }; while (toto < MAX_VALUES){ if (test > MIN_VALUES){ } // if } // while