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
Clean Code JavaScript
Riza Fahmi
 
PPTX
clean code book summary - uncle bob - English version
saber tabatabaee
 
KEY
Clean code and Code Smells
Mario Sangiorgio
 
PDF
Javascript Clean Code
Petra Barus
 
PDF
Clean code
Alvaro García Loaisa
 
PPTX
Clean code slide
Anh Huan Miu
 
PPTX
Clean code
Mahmoud Zizo
 
PPTX
Clean Code
Dmytro Turskyi
 
PPTX
Clean code
Henrique Smoco
 
PDF
TypeScript Introduction
Dmitry Sheiko
 
PPTX
Clean Code I - Best Practices
Theo Jungeblut
 
PDF
Clean code
Arturo Herrero
 
PDF
Clean coding-practices
John Ferguson Smart Limited
 
PPTX
Why TypeScript?
FITC
 
PPTX
Clean code
ifnu bima
 
PDF
Clean Code
ISchwarz23
 
PDF
Webrender 1.0
Daosheng Mu
 
PDF
TypeScript - An Introduction
NexThoughts Technologies
 
PDF
JUnit & Mockito, first steps
Renato Primavera
 
PPTX
Introduction to es6
NexThoughts Technologies
 
Clean Code JavaScript
Riza Fahmi
 
clean code book summary - uncle bob - English version
saber tabatabaee
 
Clean code and Code Smells
Mario Sangiorgio
 
Javascript Clean Code
Petra Barus
 
Clean code slide
Anh Huan Miu
 
Clean code
Mahmoud Zizo
 
Clean Code
Dmytro Turskyi
 
Clean code
Henrique Smoco
 
TypeScript Introduction
Dmitry Sheiko
 
Clean Code I - Best Practices
Theo Jungeblut
 
Clean code
Arturo Herrero
 
Clean coding-practices
John Ferguson Smart Limited
 
Why TypeScript?
FITC
 
Clean code
ifnu bima
 
Clean Code
ISchwarz23
 
Webrender 1.0
Daosheng Mu
 
TypeScript - An Introduction
NexThoughts Technologies
 
JUnit & Mockito, first steps
Renato Primavera
 
Introduction to es6
NexThoughts Technologies
 

Similar to Clean code in JavaScript (20)

PDF
Good Coding Practices with JavaScript
🏁 Pierre-Henry Soria 💡
 
PDF
OOP Best Practices in JavaScript
Haim Michael
 
PDF
Clean Code. An Agile Guide to Software Craft Kameron H.
komvjzfjj621
 
PDF
Clean code and code smells
Md. Aftab Uddin Kajal
 
PDF
Writing clean code
Angel Garcia Olloqui
 
PDF
Clean code
Khou Suylong
 
PDF
Beyond PITS, Functional Principles for Software Architecture
Jayaram Sankaranarayanan
 
PDF
JavaScript Abstraction
☆ Milan Adamovsky ☆
 
PDF
379008-rc217-functionalprogramming
Luis Atencio
 
PPT
Wakanday JS201 Best Practices
Juergen Fesslmeier
 
PPTX
Clean Code - Writing code for human
NETKO Solution
 
PDF
Does your code spark joy? Refactoring techniques to make your life easier.
Juciellen Cabrera
 
PPTX
Clean Code (David Frame) - PHPBelfast Meetup 22/02/24
davidgrantframe
 
ODP
From object oriented to functional domain modeling
Codemotion
 
PPTX
Cleaner Code - CodeStock 2019 Edition
Dave Fancher
 
PDF
From object oriented to functional domain modeling
Mario Fusco
 
PDF
Functional Programming 101 for Java 7 Developers
Jayaram Sankaranarayanan
 
PDF
Functional Programming Patterns (BuildStuff '14)
Scott Wlaschin
 
PDF
[DevDay2018] Let’s all get along. Clean Code please! - By: Christophe K. Ngo,...
DevDay Da Nang
 
PPTX
Javascript Programming according to Industry Standards.pptx
MukundSonaiya1
 
Good Coding Practices with JavaScript
🏁 Pierre-Henry Soria 💡
 
OOP Best Practices in JavaScript
Haim Michael
 
Clean Code. An Agile Guide to Software Craft Kameron H.
komvjzfjj621
 
Clean code and code smells
Md. Aftab Uddin Kajal
 
Writing clean code
Angel Garcia Olloqui
 
Clean code
Khou Suylong
 
Beyond PITS, Functional Principles for Software Architecture
Jayaram Sankaranarayanan
 
JavaScript Abstraction
☆ Milan Adamovsky ☆
 
379008-rc217-functionalprogramming
Luis Atencio
 
Wakanday JS201 Best Practices
Juergen Fesslmeier
 
Clean Code - Writing code for human
NETKO Solution
 
Does your code spark joy? Refactoring techniques to make your life easier.
Juciellen Cabrera
 
Clean Code (David Frame) - PHPBelfast Meetup 22/02/24
davidgrantframe
 
From object oriented to functional domain modeling
Codemotion
 
Cleaner Code - CodeStock 2019 Edition
Dave Fancher
 
From object oriented to functional domain modeling
Mario Fusco
 
Functional Programming 101 for Java 7 Developers
Jayaram Sankaranarayanan
 
Functional Programming Patterns (BuildStuff '14)
Scott Wlaschin
 
[DevDay2018] Let’s all get along. Clean Code please! - By: Christophe K. Ngo,...
DevDay Da Nang
 
Javascript Programming according to Industry Standards.pptx
MukundSonaiya1
 
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)

PDF
LOOPS in C Programming Language - Technology
RishabhDwivedi43
 
PDF
"Beyond English: Navigating the Challenges of Building a Ukrainian-language R...
Fwdays
 
PDF
Peak of Data & AI Encore AI-Enhanced Workflows for the Real World
Safe Software
 
PDF
Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
PDF
CIFDAQ Market Insights for July 7th 2025
CIFDAQ
 
PDF
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 
PDF
New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
PPTX
COMPARISON OF RASTER ANALYSIS TOOLS OF QGIS AND ARCGIS
Sharanya Sarkar
 
PDF
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
PDF
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
PPTX
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
PDF
Exolore The Essential AI Tools in 2025.pdf
Srinivasan M
 
PDF
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 
PDF
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
PDF
Jak MŚP w Europie Środkowo-Wschodniej odnajdują się w świecie AI
dominikamizerska1
 
PDF
The 2025 InfraRed Report - Redpoint Ventures
Razin Mustafiz
 
PDF
Reverse Engineering of Security Products: Developing an Advanced Microsoft De...
nwbxhhcyjv
 
PDF
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
PDF
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
PPTX
Future Tech Innovations 2025 – A TechLists Insight
TechLists
 
LOOPS in C Programming Language - Technology
RishabhDwivedi43
 
"Beyond English: Navigating the Challenges of Building a Ukrainian-language R...
Fwdays
 
Peak of Data & AI Encore AI-Enhanced Workflows for the Real World
Safe Software
 
Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
CIFDAQ Market Insights for July 7th 2025
CIFDAQ
 
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 
New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
COMPARISON OF RASTER ANALYSIS TOOLS OF QGIS AND ARCGIS
Sharanya Sarkar
 
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
Exolore The Essential AI Tools in 2025.pdf
Srinivasan M
 
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
Jak MŚP w Europie Środkowo-Wschodniej odnajdują się w świecie AI
dominikamizerska1
 
The 2025 InfraRed Report - Redpoint Ventures
Razin Mustafiz
 
Reverse Engineering of Security Products: Developing an Advanced Microsoft De...
nwbxhhcyjv
 
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
Future Tech Innovations 2025 – A TechLists Insight
TechLists
 

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