Frames

Untitled

0
1# clean-code-javascript
2
3## Table of Contents
4
51. [Introduction](#introduction)
62. [Variables](#variables)
73. [Functions](#functions)
84. [Objects and Data Structures](#objects-and-data-structures)
95. [Classes](#classes)
106. [SOLID](#solid)
117. [Testing](#testing)
128. [Concurrency](#concurrency)
139. [Error Handling](#error-handling)
1410. [Formatting](#formatting)
1511. [Comments](#comments)
1612. [Translation](#translation)
17
18## Introduction
19
20![Humorous image of software quality estimation as a count of how many expletives
21you shout when reading code](https://www.osnews.com/images/comics/wtfm.jpg)
22
23Software engineering principles, from Robert C. Martin's book
24[_Clean Code_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882),
25adapted for JavaScript. This is not a style guide. It's a guide to producing
26[readable, reusable, and refactorable](https://github.com/ryanmcdermott/3rs-of-software-architecture) software in JavaScript.
27
28Not every principle herein has to be strictly followed, and even fewer will be
29universally agreed upon. These are guidelines and nothing more, but they are
30ones codified over many years of collective experience by the authors of
31_Clean Code_.
32
33Our craft of software engineering is just a bit over 50 years old, and we are
34still learning a lot. When software architecture is as old as architecture
35itself, maybe then we will have harder rules to follow. For now, let these
36guidelines serve as a touchstone by which to assess the quality of the
37JavaScript code that you and your team produce.
38
39One more thing: knowing these won't immediately make you a better software
40developer, and working with them for many years doesn't mean you won't make
41mistakes. Every piece of code starts as a first draft, like wet clay getting
42shaped into its final form. Finally, we chisel away the imperfections when
43we review it with our peers. Don't beat yourself up for first drafts that need
44improvement. Beat up the code instead!
45
46## **Variables**
47
48### Use meaningful and pronounceable variable names
49
50**Bad:**
51
52```javascript
53const yyyymmdstr = moment().format("YYYY/MM/DD");
54```
55
56**Good:**
57
58```javascript
59const currentDate = moment().format("YYYY/MM/DD");
60```
61
62**[⬆ back to top](#table-of-contents)**
63
64### Use the same vocabulary for the same type of variable
65
66**Bad:**
67
68```javascript
69getUserInfo();
70getClientData();
71getCustomerRecord();
72```
73
74**Good:**
75
76```javascript
77getUser();
78```
79
80**[⬆ back to top](#table-of-contents)**
81
82### Use searchable names
83
84We will read more code than we will ever write. It's important that the code we
85do write is readable and searchable. By _not_ naming variables that end up
86being meaningful for understanding our program, we hurt our readers.
87Make your names searchable. Tools like
88[buddy.js](https://github.com/danielstjules/buddy.js) and
89[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md)
90can help identify unnamed constants.
91
92**Bad:**
93
94```javascript
95// What the heck is 86400000 for?
96setTimeout(blastOff, 86400000);
97```
98
99**Good:**
100
101```javascript
102// Declare them as capitalized named constants.
103const MILLISECONDS_IN_A_DAY = 86400000;
104
105setTimeout(blastOff, MILLISECONDS_IN_A_DAY);
106```
107
108**[⬆ back to top](#table-of-contents)**
109
110### Use explanatory variables
111
112**Bad:**
113
114```javascript
115const address = "One Infinite Loop, Cupertino 95014";
116const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
117saveCityZipCode(
118 address.match(cityZipCodeRegex)[1],
119 address.match(cityZipCodeRegex)[2]
120);
121```
122
123**Good:**
124
125```javascript
126const address = "One Infinite Loop, Cupertino 95014";
127const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
128const [, city, zipCode] = address.match(cityZipCodeRegex) || [];
129saveCityZipCode(city, zipCode);
130```
131
132**[⬆ back to top](#table-of-contents)**
133
134### Avoid Mental Mapping
135
136Explicit is better than implicit.
137
138**Bad:**
139
140```javascript
141const locations = ["Austin", "New York", "San Francisco"];
142locations.forEach(l => {
143 doStuff();
144 doSomeOtherStuff();
145 // ...
146 // ...
147 // ...
148 // Wait, what is `l` for again?
149 dispatch(l);
150});
151```
152
153**Good:**
154
155```javascript
156const locations = ["Austin", "New York", "San Francisco"];
157locations.forEach(location => {
158 doStuff();
159 doSomeOtherStuff();
160 // ...
161 // ...
162 // ...
163 dispatch(location);
164});
165```
166
167**[⬆ back to top](#table-of-contents)**
168
169### Don't add unneeded context
170
171If your class/object name tells you something, don't repeat that in your
172variable name.
173
174**Bad:**
175
176```javascript
177const Car = {
178 carMake: "Honda",
179 carModel: "Accord",
180 carColor: "Blue"
181};
182
183function paintCar(car) {
184 car.carColor = "Red";
185}
186```
187
188**Good:**
189
190```javascript
191const Car = {
192 make: "Honda",
193 model: "Accord",
194 color: "Blue"
195};
196
197function paintCar(car) {
198 car.color = "Red";
199}
200```
201
202**[⬆ back to top](#table-of-contents)**
203
204### Use default arguments instead of short circuiting or conditionals
205
206Default arguments are often cleaner than short circuiting. Be aware that if you
207use them, your function will only provide default values for `undefined`
208arguments. Other "falsy" values such as `''`, `""`, `false`, `null`, `0`, and
209`NaN`, will not be replaced by a default value.
210
211**Bad:**
212
213```javascript
214function createMicrobrewery(name) {
215 const breweryName = name || "Hipster Brew Co.";
216 // ...
217}
218```
219
220**Good:**
221
222```javascript
223function createMicrobrewery(name = "Hipster Brew Co.") {
224 // ...
225}
226```
227
228**[⬆ back to top](#table-of-contents)**
229
230## **Functions**
231
232### Function arguments (2 or fewer ideally)
233
234Limiting the amount of function parameters is incredibly important because it
235makes testing your function easier. Having more than three leads to a
236combinatorial explosion where you have to test tons of different cases with
237each separate argument.
238
239One or two arguments is the ideal case, and three should be avoided if possible.
240Anything more than that should be consolidated. Usually, if you have
241more than two arguments then your function is trying to do too much. In cases
242where it's not, most of the time a higher-level object will suffice as an
243argument.
244
245Since JavaScript allows you to make objects on the fly, without a lot of class
246boilerplate, you can use an object if you are finding yourself needing a
247lot of arguments.
248
249To make it obvious what properties the function expects, you can use the ES2015/ES6
250destructuring syntax. This has a few advantages:
251
2521. When someone looks at the function signature, it's immediately clear what
253 properties are being used.
2542. Destructuring also clones the specified primitive values of the argument
255 object passed into the function. This can help prevent side effects. Note:
256 objects and arrays that are destructured from the argument object are NOT
257 cloned.
2583. Linters can warn you about unused properties, which would be impossible
259 without destructuring.
260
261**Bad:**
262
263```javascript
264function createMenu(title, body, buttonText, cancellable) {
265 // ...
266}
267```
268
269**Good:**
270
271```javascript
272function createMenu({ title, body, buttonText, cancellable }) {
273 // ...
274}
275
276createMenu({
277 title: "Foo",
278 body: "Bar",
279 buttonText: "Baz",
280 cancellable: true
281});
282```
283
284**[⬆ back to top](#table-of-contents)**
285
286### Functions should do one thing
287
288This is by far the most important rule in software engineering. When functions
289do more than one thing, they are harder to compose, test, and reason about.
290When you can isolate a function to just one action, they can be refactored
291easily and your code will read much cleaner. If you take nothing else away from
292this guide other than this, you'll be ahead of many developers.
293
294**Bad:**
295
296```javascript
297function emailClients(clients) {
298 clients.forEach(client => {
299 const clientRecord = database.lookup(client);
300 if (clientRecord.isActive()) {
301 email(client);
302 }
303 });
304}
305```
306
307**Good:**
308
309```javascript
310function emailActiveClients(clients) {
311 clients.filter(isActiveClient).forEach(email);
312}
313
314function isActiveClient(client) {
315 const clientRecord = database.lookup(client);
316 return clientRecord.isActive();
317}
318```
319
320**[⬆ back to top](#table-of-contents)**
321
322### Function names should say what they do
323
324**Bad:**
325
326```javascript
327function addToDate(date, month) {
328 // ...
329}
330
331const date = new Date();
332
333// It's hard to tell from the function name what is added
334addToDate(date, 1);
335```
336
337**Good:**
338
339```javascript
340function addMonthToDate(month, date) {
341 // ...
342}
343
344const date = new Date();
345addMonthToDate(1, date);
346```
347
348**[⬆ back to top](#table-of-contents)**
349
350### Functions should only be one level of abstraction
351
352When you have more than one level of abstraction your function is usually
353doing too much. Splitting up functions leads to reusability and easier
354testing.
355
356**Bad:**
357
358```javascript
359function parseBetterJSAlternative(code) {
360 const REGEXES = [
361 // ...
362 ];
363
364 const statements = code.split(" ");
365 const tokens = [];
366 REGEXES.forEach(REGEX => {
367 statements.forEach(statement => {
368 // ...
369 });
370 });
371
372 const ast = [];
373 tokens.forEach(token => {
374 // lex...
375 });
376
377 ast.forEach(node => {
378 // parse...
379 });
380}
381```
382
383**Good:**
384
385```javascript
386function parseBetterJSAlternative(code) {
387 const tokens = tokenize(code);
388 const syntaxTree = parse(tokens);
389 syntaxTree.forEach(node => {
390 // parse...
391 });
392}
393
394function tokenize(code) {
395 const REGEXES = [
396 // ...
397 ];
398
399 const statements = code.split(" ");
400 const tokens = [];
401 REGEXES.forEach(REGEX => {
402 statements.forEach(statement => {
403 tokens.push(/* ... */);
404 });
405 });
406
407 return tokens;
408}
409
410function parse(tokens) {
411 const syntaxTree = [];
412 tokens.forEach(token => {
413 syntaxTree.push(/* ... */);
414 });
415
416 return syntaxTree;
417}
418```
419
420**[⬆ back to top](#table-of-contents)**
421
422### Remove duplicate code
423
424Do your absolute best to avoid duplicate code. Duplicate code is bad because it
425means that there's more than one place to alter something if you need to change
426some logic.
427
428Imagine if you run a restaurant and you keep track of your inventory: all your
429tomatoes, onions, garlic, spices, etc. If you have multiple lists that
430you keep this on, then all have to be updated when you serve a dish with
431tomatoes in them. If you only have one list, there's only one place to update!
432
433Oftentimes you have duplicate code because you have two or more slightly
434different things, that share a lot in common, but their differences force you
435to have two or more separate functions that do much of the same things. Removing
436duplicate code means creating an abstraction that can handle this set of
437different things with just one function/module/class.
438
439Getting the abstraction right is critical, that's why you should follow the
440SOLID principles laid out in the _Classes_ section. Bad abstractions can be
441worse than duplicate code, so be careful! Having said this, if you can make
442a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself
443updating multiple places anytime you want to change one thing.
444
445**Bad:**
446
447```javascript
448function showDeveloperList(developers) {
449 developers.forEach(developer => {
450 const expectedSalary = developer.calculateExpectedSalary();
451 const experience = developer.getExperience();
452 const githubLink = developer.getGithubLink();
453 const data = {
454 expectedSalary,
455 experience,
456 githubLink
457 };
458
459 render(data);
460 });
461}
462
463function showManagerList(managers) {
464 managers.forEach(manager => {
465 const expectedSalary = manager.calculateExpectedSalary();
466 const experience = manager.getExperience();
467 const portfolio = manager.getMBAProjects();
468 const data = {
469 expectedSalary,
470 experience,
471 portfolio
472 };
473
474 render(data);
475 });
476}
477```
478
479**Good:**
480
481```javascript
482function showEmployeeList(employees) {
483 employees.forEach(employee => {
484 const expectedSalary = employee.calculateExpectedSalary();
485 const experience = employee.getExperience();
486
487 const data = {
488 expectedSalary,
489 experience
490 };
491
492 switch (employee.type) {
493 case "manager":
494 data.portfolio = employee.getMBAProjects();
495 break;
496 case "developer":
497 data.githubLink = employee.getGithubLink();
498 break;
499 }
500
501 render(data);
502 });
503}
504```
505
506**[⬆ back to top](#table-of-contents)**
507
508### Set default objects with Object.assign
509
510**Bad:**
511
512```javascript
513const menuConfig = {
514 title: null,
515 body: "Bar",
516 buttonText: null,
517 cancellable: true
518};
519
520function createMenu(config) {
521 config.title = config.title || "Foo";
522 config.body = config.body || "Bar";
523 config.buttonText = config.buttonText || "Baz";
524 config.cancellable =
525 config.cancellable !== undefined ? config.cancellable : true;
526}
527
528createMenu(menuConfig);
529```
530
531**Good:**
532
533```javascript
534const menuConfig = {
535 title: "Order",
536 // User did not include 'body' key
537 buttonText: "Send",
538 cancellable: true
539};
540
541function createMenu(config) {
542 config = Object.assign(
543 {
544 title: "Foo",
545 body: "Bar",
546 buttonText: "Baz",
547 cancellable: true
548 },
549 config
550 );
551
552 // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
553 // ...
554}
555
556createMenu(menuConfig);
557```
558
559**[⬆ back to top](#table-of-contents)**
560
561### Don't use flags as function parameters
562
563Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean.
564
565**Bad:**
566
567```javascript
568function createFile(name, temp) {
569 if (temp) {
570 fs.create(`./temp/${name}`);
571 } else {
572 fs.create(name);
573 }
574}
575```
576
577**Good:**
578
579```javascript
580function createFile(name) {
581 fs.create(name);
582}
583
584function createTempFile(name) {
585 createFile(`./temp/${name}`);
586}
587```
588
589**[⬆ back to top](#table-of-contents)**
590
591### Avoid Side Effects (part 1)
592
593A function produces a side effect if it does anything other than take a value in
594and return another value or values. A side effect could be writing to a file,
595modifying some global variable, or accidentally wiring all your money to a
596stranger.
597
598Now, you do need to have side effects in a program on occasion. Like the previous
599example, you might need to write to a file. What you want to do is to
600centralize where you are doing this. Don't have several functions and classes
601that write to a particular file. Have one service that does it. One and only one.
602
603The main point is to avoid common pitfalls like sharing state between objects
604without any structure, using mutable data types that can be written to by anything,
605and not centralizing where your side effects occur. If you can do this, you will
606be happier than the vast majority of other programmers.
607
608**Bad:**
609
610```javascript
611// Global variable referenced by following function.
612// If we had another function that used this name, now it'd be an array and it could break it.
613let name = "Ryan McDermott";
614
615function splitIntoFirstAndLastName() {
616 name = name.split(" ");
617}
618
619splitIntoFirstAndLastName();
620
621console.log(name); // ['Ryan', 'McDermott'];
622```
623
624**Good:**
625
626```javascript
627function splitIntoFirstAndLastName(name) {
628 return name.split(" ");
629}
630
631const name = "Ryan McDermott";
632const newName = splitIntoFirstAndLastName(name);
633
634console.log(name); // 'Ryan McDermott';
635console.log(newName); // ['Ryan', 'McDermott'];
636```
637
638**[⬆ back to top](#table-of-contents)**
639
640### Avoid Side Effects (part 2)
641
642In JavaScript, primitives are passed by value and objects/arrays are passed by
643reference. In the case of objects and arrays, if your function makes a change
644in a shopping cart array, for example, by adding an item to purchase,
645then any other function that uses that `cart` array will be affected by this
646addition. That may be great, however it can be bad too. Let's imagine a bad
647situation:
648
649The user clicks the "Purchase", button which calls a `purchase` function that
650spawns a network request and sends the `cart` array to the server. Because
651of a bad network connection, the `purchase` function has to keep retrying the
652request. Now, what if in the meantime the user accidentally clicks "Add to Cart"
653button on an item they don't actually want before the network request begins?
654If that happens and the network request begins, then that purchase function
655will send the accidentally added item because it has a reference to a shopping
656cart array that the `addItemToCart` function modified by adding an unwanted
657item.
658
659A great solution would be for the `addItemToCart` to always clone the `cart`,
660edit it, and return the clone. This ensures that no other functions that are
661holding onto a reference of the shopping cart will be affected by any changes.
662
663Two caveats to mention to this approach:
664
6651. There might be cases where you actually want to modify the input object,
666 but when you adopt this programming practice you will find that those cases
667 are pretty rare. Most things can be refactored to have no side effects!
668
6692. Cloning big objects can be very expensive in terms of performance. Luckily,
670 this isn't a big issue in practice because there are
671 [great libraries](https://facebook.github.io/immutable-js/) that allow
672 this kind of programming approach to be fast and not as memory intensive as
673 it would be for you to manually clone objects and arrays.
674
675**Bad:**
676
677```javascript
678const addItemToCart = (cart, item) => {
679 cart.push({ item, date: Date.now() });
680};
681```
682
683**Good:**
684
685```javascript
686const addItemToCart = (cart, item) => {
687 return [...cart, { item, date: Date.now() }];
688};
689```
690
691**[⬆ back to top](#table-of-contents)**
692
693### Don't write to global functions
694
695Polluting globals is a bad practice in JavaScript because you could clash with another
696library and the user of your API would be none-the-wiser until they get an
697exception in production. Let's think about an example: what if you wanted to
698extend JavaScript's native Array method to have a `diff` method that could
699show the difference between two arrays? You could write your new function
700to the `Array.prototype`, but it could clash with another library that tried
701to do the same thing. What if that other library was just using `diff` to find
702the difference between the first and last elements of an array? This is why it
703would be much better to just use ES2015/ES6 classes and simply extend the `Array` global.
704
705**Bad:**
706
707```javascript
708Array.prototype.diff = function diff(comparisonArray) {
709 const hash = new Set(comparisonArray);
710 return this.filter(elem => !hash.has(elem));
711};
712```
713
714**Good:**
715
716```javascript
717class SuperArray extends Array {
718 diff(comparisonArray) {
719 const hash = new Set(comparisonArray);
720 return this.filter(elem => !hash.has(elem));
721 }
722}
723```
724
725**[⬆ back to top](#table-of-contents)**
726
727### Favor functional programming over imperative programming
728
729JavaScript isn't a functional language in the way that Haskell is, but it has
730a functional flavor to it. Functional languages can be cleaner and easier to test.
731Favor this style of programming when you can.
732
733**Bad:**
734
735```javascript
736const programmerOutput = [
737 {
738 name: "Uncle Bobby",
739 linesOfCode: 500
740 },
741 {
742 name: "Suzie Q",
743 linesOfCode: 1500
744 },
745 {
746 name: "Jimmy Gosling",
747 linesOfCode: 150
748 },
749 {
750 name: "Gracie Hopper",
751 linesOfCode: 1000
752 }
753];
754
755let totalOutput = 0;
756
757for (let i = 0; i < programmerOutput.length; i++) {
758 totalOutput += programmerOutput[i].linesOfCode;
759}
760```
761
762**Good:**
763
764```javascript
765const programmerOutput = [
766 {
767 name: "Uncle Bobby",
768 linesOfCode: 500
769 },
770 {
771 name: "Suzie Q",
772 linesOfCode: 1500
773 },
774 {
775 name: "Jimmy Gosling",
776 linesOfCode: 150
777 },
778 {
779 name: "Gracie Hopper",
780 linesOfCode: 1000
781 }
782];
783
784const totalOutput = programmerOutput.reduce(
785 (totalLines, output) => totalLines + output.linesOfCode,
786 0
787);
788```
789
790**[⬆ back to top](#table-of-contents)**
791
792### Encapsulate conditionals
793
794**Bad:**
795
796```javascript
797if (fsm.state === "fetching" && isEmpty(listNode)) {
798 // ...
799}
800```
801
802**Good:**
803
804```javascript
805function shouldShowSpinner(fsm, listNode) {
806 return fsm.state === "fetching" && isEmpty(listNode);
807}
808
809if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
810 // ...
811}
812```
813
814**[⬆ back to top](#table-of-contents)**
815
816### Avoid negative conditionals
817
818**Bad:**
819
820```javascript
821function isDOMNodeNotPresent(node) {
822 // ...
823}
824
825if (!isDOMNodeNotPresent(node)) {
826 // ...
827}
828```
829
830**Good:**
831
832```javascript
833function isDOMNodePresent(node) {
834 // ...
835}
836
837if (isDOMNodePresent(node)) {
838 // ...
839}
840```
841
842**[⬆ back to top](#table-of-contents)**
843
844### Avoid conditionals
845
846This seems like an impossible task. Upon first hearing this, most people say,
847"how am I supposed to do anything without an `if` statement?" The answer is that
848you can use polymorphism to achieve the same task in many cases. The second
849question is usually, "well that's great but why would I want to do that?" The
850answer is a previous clean code concept we learned: a function should only do
851one thing. When you have classes and functions that have `if` statements, you
852are telling your user that your function does more than one thing. Remember,
853just do one thing.
854
855**Bad:**
856
857```javascript
858class Airplane {
859 // ...
860 getCruisingAltitude() {
861 switch (this.type) {
862 case "777":
863 return this.getMaxAltitude() - this.getPassengerCount();
864 case "Air Force One":
865 return this.getMaxAltitude();
866 case "Cessna":
867 return this.getMaxAltitude() - this.getFuelExpenditure();
868 }
869 }
870}
871```
872
873**Good:**
874
875```javascript
876class Airplane {
877 // ...
878}
879
880class Boeing777 extends Airplane {
881 // ...
882 getCruisingAltitude() {
883 return this.getMaxAltitude() - this.getPassengerCount();
884 }
885}
886
887class AirForceOne extends Airplane {
888 // ...
889 getCruisingAltitude() {
890 return this.getMaxAltitude();
891 }
892}
893
894class Cessna extends Airplane {
895 // ...
896 getCruisingAltitude() {
897 return this.getMaxAltitude() - this.getFuelExpenditure();
898 }
899}
900```
901
902**[⬆ back to top](#table-of-contents)**
903
904### Avoid type-checking (part 1)
905
906JavaScript is untyped, which means your functions can take any type of argument.
907Sometimes you are bitten by this freedom and it becomes tempting to do
908type-checking in your functions. There are many ways to avoid having to do this.
909The first thing to consider is consistent APIs.
910
911**Bad:**
912
913```javascript
914function travelToTexas(vehicle) {
915 if (vehicle instanceof Bicycle) {
916 vehicle.pedal(this.currentLocation, new Location("texas"));
917 } else if (vehicle instanceof Car) {
918 vehicle.drive(this.currentLocation, new Location("texas"));
919 }
920}
921```
922
923**Good:**
924
925```javascript
926function travelToTexas(vehicle) {
927 vehicle.move(this.currentLocation, new Location("texas"));
928}
929```
930
931**[⬆ back to top](#table-of-contents)**
932
933### Avoid type-checking (part 2)
934
935If you are working with basic primitive values like strings and integers,
936and you can't use polymorphism but you still feel the need to type-check,
937you should consider using TypeScript. It is an excellent alternative to normal
938JavaScript, as it provides you with static typing on top of standard JavaScript
939syntax. The problem with manually type-checking normal JavaScript is that
940doing it well requires so much extra verbiage that the faux "type-safety" you get
941doesn't make up for the lost readability. Keep your JavaScript clean, write
942good tests, and have good code reviews. Otherwise, do all of that but with
943TypeScript (which, like I said, is a great alternative!).
944
945**Bad:**
946
947```javascript
948function combine(val1, val2) {
949 if (
950 (typeof val1 === "number" && typeof val2 === "number") ||
951 (typeof val1 === "string" && typeof val2 === "string")
952 ) {
953 return val1 + val2;
954 }
955
956 throw new Error("Must be of type String or Number");
957}
958```
959
960**Good:**
961
962```javascript
963function combine(val1, val2) {
964 return val1 + val2;
965}
966```
967
968**[⬆ back to top](#table-of-contents)**
969
970### Don't over-optimize
971
972Modern browsers do a lot of optimization under-the-hood at runtime. A lot of
973times, if you are optimizing then you are just wasting your time. [There are good
974resources](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers)
975for seeing where optimization is lacking. Target those in the meantime, until
976they are fixed if they can be.
977
978**Bad:**
979
980```javascript
981// On old browsers, each iteration with uncached `list.length` would be costly
982// because of `list.length` recomputation. In modern browsers, this is optimized.
983for (let i = 0; len = list.length; i < len; i++) {
984 // ...
985}
986```
987
988**Good:**
989
990```javascript
991for (let i = 0; i < list.length; i++) {
992 // ...
993}
994```
995
996**[⬆ back to top](#table-of-contents)**
997
998### Remove dead code
999
1000Dead code is just as bad as duplicate code. There's no reason to keep it in
1001your codebase. If it's not being called, get rid of it! It will still be safe
1002in your version history if you still need it.
1003
1004**Bad:**
1005
1006```javascript
1007function oldRequestModule(url) {
1008 // ...
1009}
1010
1011function newRequestModule(url) {
1012 // ...
1013}
1014
1015const req = newRequestModule;
1016inventoryTracker("apples", req, "www.inventory-awesome.io");
1017```
1018
1019**Good:**
1020
1021```javascript
1022function newRequestModule(url) {
1023 // ...
1024}
1025
1026const req = newRequestModule;
1027inventoryTracker("apples", req, "www.inventory-awesome.io");
1028```
1029
1030**[⬆ back to top](#table-of-contents)**
1031
1032## **Objects and Data Structures**
1033
1034### Use getters and setters
1035
1036Using getters and setters to access data on objects could be better than simply
1037looking for a property on an object. "Why?" you might ask. Well, here's an
1038unorganized list of reasons why:
1039
1040- When you want to do more beyond getting an object property, you don't have
1041 to look up and change every accessor in your codebase.
1042- Makes adding validation simple when doing a `set`.
1043- Encapsulates the internal representation.
1044- Easy to add logging and error handling when getting and setting.
1045- You can lazy load your object's properties, let's say getting it from a
1046 server.
1047
1048**Bad:**
1049
1050```javascript
1051function makeBankAccount() {
1052 // ...
1053
1054 return {
1055 balance: 0
1056 // ...
1057 };
1058}
1059
1060const account = makeBankAccount();
1061account.balance = 100;
1062```
1063
1064**Good:**
1065
1066```javascript
1067function makeBankAccount() {
1068 // this one is private
1069 let balance = 0;
1070
1071 // a "getter", made public via the returned object below
1072 function getBalance() {
1073 return balance;
1074 }
1075
1076 // a "setter", made public via the returned object below
1077 function setBalance(amount) {
1078 // ... validate before updating the balance
1079 balance = amount;
1080 }
1081
1082 return {
1083 // ...
1084 getBalance,
1085 setBalance
1086 };
1087}
1088
1089const account = makeBankAccount();
1090account.setBalance(100);
1091```
1092
1093**[⬆ back to top](#table-of-contents)**
1094
1095### Make objects have private members
1096
1097This can be accomplished through closures (for ES5 and below).
1098
1099**Bad:**
1100
1101```javascript
1102const Employee = function(name) {
1103 this.name = name;
1104};
1105
1106Employee.prototype.getName = function getName() {
1107 return this.name;
1108};
1109
1110const employee = new Employee("John Doe");
1111console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
1112delete employee.name;
1113console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined
1114```
1115
1116**Good:**
1117
1118```javascript
1119function makeEmployee(name) {
1120 return {
1121 getName() {
1122 return name;
1123 }
1124 };
1125}
1126
1127const employee = makeEmployee("John Doe");
1128console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
1129delete employee.name;
1130console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
1131```
1132
1133**[⬆ back to top](#table-of-contents)**
1134
1135## **Classes**
1136
1137### Prefer ES2015/ES6 classes over ES5 plain functions
1138
1139It's very difficult to get readable class inheritance, construction, and method
1140definitions for classical ES5 classes. If you need inheritance (and be aware
1141that you might not), then prefer ES2015/ES6 classes. However, prefer small functions over
1142classes until you find yourself needing larger and more complex objects.
1143
1144**Bad:**
1145
1146```javascript
1147const Animal = function(age) {
1148 if (!(this instanceof Animal)) {
1149 throw new Error("Instantiate Animal with `new`");
1150 }
1151
1152 this.age = age;
1153};
1154
1155Animal.prototype.move = function move() {};
1156
1157const Mammal = function(age, furColor) {
1158 if (!(this instanceof Mammal)) {
1159 throw new Error("Instantiate Mammal with `new`");
1160 }
1161
1162 Animal.call(this, age);
1163 this.furColor = furColor;
1164};
1165
1166Mammal.prototype = Object.create(Animal.prototype);
1167Mammal.prototype.constructor = Mammal;
1168Mammal.prototype.liveBirth = function liveBirth() {};
1169
1170const Human = function(age, furColor, languageSpoken) {
1171 if (!(this instanceof Human)) {
1172 throw new Error("Instantiate Human with `new`");
1173 }
1174
1175 Mammal.call(this, age, furColor);
1176 this.languageSpoken = languageSpoken;
1177};
1178
1179Human.prototype = Object.create(Mammal.prototype);
1180Human.prototype.constructor = Human;
1181Human.prototype.speak = function speak() {};
1182```
1183
1184**Good:**
1185
1186```javascript
1187class Animal {
1188 constructor(age) {
1189 this.age = age;
1190 }
1191
1192 move() {
1193 /* ... */
1194 }
1195}
1196
1197class Mammal extends Animal {
1198 constructor(age, furColor) {
1199 super(age);
1200 this.furColor = furColor;
1201 }
1202
1203 liveBirth() {
1204 /* ... */
1205 }
1206}
1207
1208class Human extends Mammal {
1209 constructor(age, furColor, languageSpoken) {
1210 super(age, furColor);
1211 this.languageSpoken = languageSpoken;
1212 }
1213
1214 speak() {
1215 /* ... */
1216 }
1217}
1218```
1219
1220**[⬆ back to top](#table-of-contents)**
1221
1222### Use method chaining
1223
1224This pattern is very useful in JavaScript and you see it in many libraries such
1225as jQuery and Lodash. It allows your code to be expressive, and less verbose.
1226For that reason, I say, use method chaining and take a look at how clean your code
1227will be. In your class functions, simply return `this` at the end of every function,
1228and you can chain further class methods onto it.
1229
1230**Bad:**
1231
1232```javascript
1233class Car {
1234 constructor(make, model, color) {
1235 this.make = make;
1236 this.model = model;
1237 this.color = color;
1238 }
1239
1240 setMake(make) {
1241 this.make = make;
1242 }
1243
1244 setModel(model) {
1245 this.model = model;
1246 }
1247
1248 setColor(color) {
1249 this.color = color;
1250 }
1251
1252 save() {
1253 console.log(this.make, this.model, this.color);
1254 }
1255}
1256
1257const car = new Car("Ford", "F-150", "red");
1258car.setColor("pink");
1259car.save();
1260```
1261
1262**Good:**
1263
1264```javascript
1265class Car {
1266 constructor(make, model, color) {
1267 this.make = make;
1268 this.model = model;
1269 this.color = color;
1270 }
1271
1272 setMake(make) {
1273 this.make = make;
1274 // NOTE: Returning this for chaining
1275 return this;
1276 }
1277
1278 setModel(model) {
1279 this.model = model;
1280 // NOTE: Returning this for chaining
1281 return this;
1282 }
1283
1284 setColor(color) {
1285 this.color = color;
1286 // NOTE: Returning this for chaining
1287 return this;
1288 }
1289
1290 save() {
1291 console.log(this.make, this.model, this.color);
1292 // NOTE: Returning this for chaining
1293 return this;
1294 }
1295}
1296
1297const car = new Car("Ford", "F-150", "red").setColor("pink").save();
1298```
1299
1300**[⬆ back to top](#table-of-contents)**
1301
1302### Prefer composition over inheritance
1303
1304As stated famously in [_Design Patterns_](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four,
1305you should prefer composition over inheritance where you can. There are lots of
1306good reasons to use inheritance and lots of good reasons to use composition.
1307The main point for this maxim is that if your mind instinctively goes for
1308inheritance, try to think if composition could model your problem better. In some
1309cases it can.
1310
1311You might be wondering then, "when should I use inheritance?" It
1312depends on your problem at hand, but this is a decent list of when inheritance
1313makes more sense than composition:
1314
13151. Your inheritance represents an "is-a" relationship and not a "has-a"
1316 relationship (Human->Animal vs. User->UserDetails).
13172. You can reuse code from the base classes (Humans can move like all animals).
13183. You want to make global changes to derived classes by changing a base class.
1319 (Change the caloric expenditure of all animals when they move).
1320
1321**Bad:**
1322
1323```javascript
1324class Employee {
1325 constructor(name, email) {
1326 this.name = name;
1327 this.email = email;
1328 }
1329
1330 // ...
1331}
1332
1333// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee
1334class EmployeeTaxData extends Employee {
1335 constructor(ssn, salary) {
1336 super();
1337 this.ssn = ssn;
1338 this.salary = salary;
1339 }
1340
1341 // ...
1342}
1343```
1344
1345**Good:**
1346
1347```javascript
1348class EmployeeTaxData {
1349 constructor(ssn, salary) {
1350 this.ssn = ssn;
1351 this.salary = salary;
1352 }
1353
1354 // ...
1355}
1356
1357class Employee {
1358 constructor(name, email) {
1359 this.name = name;
1360 this.email = email;
1361 }
1362
1363 setTaxData(ssn, salary) {
1364 this.taxData = new EmployeeTaxData(ssn, salary);
1365 }
1366 // ...
1367}
1368```
1369
1370**[⬆ back to top](#table-of-contents)**
1371
1372## **SOLID**
1373
1374### Single Responsibility Principle (SRP)
1375
1376As stated in Clean Code, "There should never be more than one reason for a class
1377to change". It's tempting to jam-pack a class with a lot of functionality, like
1378when you can only take one suitcase on your flight. The issue with this is
1379that your class won't be conceptually cohesive and it will give it many reasons
1380to change. Minimizing the amount of times you need to change a class is important.
1381It's important because if too much functionality is in one class and you modify
1382a piece of it, it can be difficult to understand how that will affect other
1383dependent modules in your codebase.
1384
1385**Bad:**
1386
1387```javascript
1388class UserSettings {
1389 constructor(user) {
1390 this.user = user;
1391 }
1392
1393 changeSettings(settings) {
1394 if (this.verifyCredentials()) {
1395 // ...
1396 }
1397 }
1398
1399 verifyCredentials() {
1400 // ...
1401 }
1402}
1403```
1404
1405**Good:**
1406
1407```javascript
1408class UserAuth {
1409 constructor(user) {
1410 this.user = user;
1411 }
1412
1413 verifyCredentials() {
1414 // ...
1415 }
1416}
1417
1418class UserSettings {
1419 constructor(user) {
1420 this.user = user;
1421 this.auth = new UserAuth(user);
1422 }
1423
1424 changeSettings(settings) {
1425 if (this.auth.verifyCredentials()) {
1426 // ...
1427 }
1428 }
1429}
1430```
1431
1432**[⬆ back to top](#table-of-contents)**
1433
1434### Open/Closed Principle (OCP)
1435
1436As stated by Bertrand Meyer, "software entities (classes, modules, functions,
1437etc.) should be open for extension, but closed for modification." What does that
1438mean though? This principle basically states that you should allow users to
1439add new functionalities without changing existing code.
1440
1441**Bad:**
1442
1443```javascript
1444class AjaxAdapter extends Adapter {
1445 constructor() {
1446 super();
1447 this.name = "ajaxAdapter";
1448 }
1449}
1450
1451class NodeAdapter extends Adapter {
1452 constructor() {
1453 super();
1454 this.name = "nodeAdapter";
1455 }
1456}
1457
1458class HttpRequester {
1459 constructor(adapter) {
1460 this.adapter = adapter;
1461 }
1462
1463 fetch(url) {
1464 if (this.adapter.name === "ajaxAdapter") {
1465 return makeAjaxCall(url).then(response => {
1466 // transform response and return
1467 });
1468 } else if (this.adapter.name === "nodeAdapter") {
1469 return makeHttpCall(url).then(response => {
1470 // transform response and return
1471 });
1472 }
1473 }
1474}
1475
1476function makeAjaxCall(url) {
1477 // request and return promise
1478}
1479
1480function makeHttpCall(url) {
1481 // request and return promise
1482}
1483```
1484
1485**Good:**
1486
1487```javascript
1488class AjaxAdapter extends Adapter {
1489 constructor() {
1490 super();
1491 this.name = "ajaxAdapter";
1492 }
1493
1494 request(url) {
1495 // request and return promise
1496 }
1497}
1498
1499class NodeAdapter extends Adapter {
1500 constructor() {
1501 super();
1502 this.name = "nodeAdapter";
1503 }
1504
1505 request(url) {
1506 // request and return promise
1507 }
1508}
1509
1510class HttpRequester {
1511 constructor(adapter) {
1512 this.adapter = adapter;
1513 }
1514
1515 fetch(url) {
1516 return this.adapter.request(url).then(response => {
1517 // transform response and return
1518 });
1519 }
1520}
1521```
1522
1523**[⬆ back to top](#table-of-contents)**
1524
1525### Liskov Substitution Principle (LSP)
1526
1527This is a scary term for a very simple concept. It's formally defined as "If S
1528is a subtype of T, then objects of type T may be replaced with objects of type S
1529(i.e., objects of type S may substitute objects of type T) without altering any
1530of the desirable properties of that program (correctness, task performed,
1531etc.)." That's an even scarier definition.
1532
1533The best explanation for this is if you have a parent class and a child class,
1534then the base class and child class can be used interchangeably without getting
1535incorrect results. This might still be confusing, so let's take a look at the
1536classic Square-Rectangle example. Mathematically, a square is a rectangle, but
1537if you model it using the "is-a" relationship via inheritance, you quickly
1538get into trouble.
1539
1540**Bad:**
1541
1542```javascript
1543class Rectangle {
1544 constructor() {
1545 this.width = 0;
1546 this.height = 0;
1547 }
1548
1549 setColor(color) {
1550 // ...
1551 }
1552
1553 render(area) {
1554 // ...
1555 }
1556
1557 setWidth(width) {
1558 this.width = width;
1559 }
1560
1561 setHeight(height) {
1562 this.height = height;
1563 }
1564
1565 getArea() {
1566 return this.width * this.height;
1567 }
1568}
1569
1570class Square extends Rectangle {
1571 setWidth(width) {
1572 this.width = width;
1573 this.height = width;
1574 }
1575
1576 setHeight(height) {
1577 this.width = height;
1578 this.height = height;
1579 }
1580}
1581
1582function renderLargeRectangles(rectangles) {
1583 rectangles.forEach(rectangle => {
1584 rectangle.setWidth(4);
1585 rectangle.setHeight(5);
1586 const area = rectangle.getArea(); // BAD: Returns 25 for Square. Should be 20.
1587 rectangle.render(area);
1588 });
1589}
1590
1591const rectangles = [new Rectangle(), new Rectangle(), new Square()];
1592renderLargeRectangles(rectangles);
1593```
1594
1595**Good:**
1596
1597```javascript
1598class Shape {
1599 setColor(color) {
1600 // ...
1601 }
1602
1603 render(area) {
1604 // ...
1605 }
1606}
1607
1608class Rectangle extends Shape {
1609 constructor(width, height) {
1610 super();
1611 this.width = width;
1612 this.height = height;
1613 }
1614
1615 getArea() {
1616 return this.width * this.height;
1617 }
1618}
1619
1620class Square extends Shape {
1621 constructor(length) {
1622 super();
1623 this.length = length;
1624 }
1625
1626 getArea() {
1627 return this.length * this.length;
1628 }
1629}
1630
1631function renderLargeShapes(shapes) {
1632 shapes.forEach(shape => {
1633 const area = shape.getArea();
1634 shape.render(area);
1635 });
1636}
1637
1638const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)];
1639renderLargeShapes(shapes);
1640```
1641
1642**[⬆ back to top](#table-of-contents)**
1643
1644### Interface Segregation Principle (ISP)
1645
1646JavaScript doesn't have interfaces so this principle doesn't apply as strictly
1647as others. However, it's important and relevant even with JavaScript's lack of
1648type system.
1649
1650ISP states that "Clients should not be forced to depend upon interfaces that
1651they do not use." Interfaces are implicit contracts in JavaScript because of
1652duck typing.
1653
1654A good example to look at that demonstrates this principle in JavaScript is for
1655classes that require large settings objects. Not requiring clients to setup
1656huge amounts of options is beneficial, because most of the time they won't need
1657all of the settings. Making them optional helps prevent having a
1658"fat interface".
1659
1660**Bad:**
1661
1662```javascript
1663class DOMTraverser {
1664 constructor(settings) {
1665 this.settings = settings;
1666 this.setup();
1667 }
1668
1669 setup() {
1670 this.rootNode = this.settings.rootNode;
1671 this.animationModule.setup();
1672 }
1673
1674 traverse() {
1675 // ...
1676 }
1677}
1678
1679const $ = new DOMTraverser({
1680 rootNode: document.getElementsByTagName("body"),
1681 animationModule() {} // Most of the time, we won't need to animate when traversing.
1682 // ...
1683});
1684```
1685
1686**Good:**
1687
1688```javascript
1689class DOMTraverser {
1690 constructor(settings) {
1691 this.settings = settings;
1692 this.options = settings.options;
1693 this.setup();
1694 }
1695
1696 setup() {
1697 this.rootNode = this.settings.rootNode;
1698 this.setupOptions();
1699 }
1700
1701 setupOptions() {
1702 if (this.options.animationModule) {
1703 // ...
1704 }
1705 }
1706
1707 traverse() {
1708 // ...
1709 }
1710}
1711
1712const $ = new DOMTraverser({
1713 rootNode: document.getElementsByTagName("body"),
1714 options: {
1715 animationModule() {}
1716 }
1717});
1718```
1719
1720**[⬆ back to top](#table-of-contents)**
1721
1722### Dependency Inversion Principle (DIP)
1723
1724This principle states two essential things:
1725
17261. High-level modules should not depend on low-level modules. Both should
1727 depend on abstractions.
17282. Abstractions should not depend upon details. Details should depend on
1729 abstractions.
1730
1731This can be hard to understand at first, but if you've worked with AngularJS,
1732you've seen an implementation of this principle in the form of Dependency
1733Injection (DI). While they are not identical concepts, DIP keeps high-level
1734modules from knowing the details of its low-level modules and setting them up.
1735It can accomplish this through DI. A huge benefit of this is that it reduces
1736the coupling between modules. Coupling is a very bad development pattern because
1737it makes your code hard to refactor.
1738
1739As stated previously, JavaScript doesn't have interfaces so the abstractions
1740that are depended upon are implicit contracts. That is to say, the methods
1741and properties that an object/class exposes to another object/class. In the
1742example below, the implicit contract is that any Request module for an
1743`InventoryTracker` will have a `requestItems` method.
1744
1745**Bad:**
1746
1747```javascript
1748class InventoryRequester {
1749 constructor() {
1750 this.REQ_METHODS = ["HTTP"];
1751 }
1752
1753 requestItem(item) {
1754 // ...
1755 }
1756}
1757
1758class InventoryTracker {
1759 constructor(items) {
1760 this.items = items;
1761
1762 // BAD: We have created a dependency on a specific request implementation.
1763 // We should just have requestItems depend on a request method: `request`
1764 this.requester = new InventoryRequester();
1765 }
1766
1767 requestItems() {
1768 this.items.forEach(item => {
1769 this.requester.requestItem(item);
1770 });
1771 }
1772}
1773
1774const inventoryTracker = new InventoryTracker(["apples", "bananas"]);
1775inventoryTracker.requestItems();
1776```
1777
1778**Good:**
1779
1780```javascript
1781class InventoryTracker {
1782 constructor(items, requester) {
1783 this.items = items;
1784 this.requester = requester;
1785 }
1786
1787 requestItems() {
1788 this.items.forEach(item => {
1789 this.requester.requestItem(item);
1790 });
1791 }
1792}
1793
1794class InventoryRequesterV1 {
1795 constructor() {
1796 this.REQ_METHODS = ["HTTP"];
1797 }
1798
1799 requestItem(item) {
1800 // ...
1801 }
1802}
1803
1804class InventoryRequesterV2 {
1805 constructor() {
1806 this.REQ_METHODS = ["WS"];
1807 }
1808
1809 requestItem(item) {
1810 // ...
1811 }
1812}
1813
1814// By constructing our dependencies externally and injecting them, we can easily
1815// substitute our request module for a fancy new one that uses WebSockets.
1816const inventoryTracker = new InventoryTracker(
1817 ["apples", "bananas"],
1818 new InventoryRequesterV2()
1819);
1820inventoryTracker.requestItems();
1821```
1822
1823**[⬆ back to top](#table-of-contents)**
1824
1825## **Testing**
1826
1827Testing is more important than shipping. If you have no tests or an
1828inadequate amount, then every time you ship code you won't be sure that you
1829didn't break anything. Deciding on what constitutes an adequate amount is up
1830to your team, but having 100% coverage (all statements and branches) is how
1831you achieve very high confidence and developer peace of mind. This means that
1832in addition to having a great testing framework, you also need to use a
1833[good coverage tool](https://gotwarlost.github.io/istanbul/).
1834
1835There's no excuse to not write tests. There are [plenty of good JS test frameworks](https://jstherightway.org/#testing-tools), so find one that your team prefers.
1836When you find one that works for your team, then aim to always write tests
1837for every new feature/module you introduce. If your preferred method is
1838Test Driven Development (TDD), that is great, but the main point is to just
1839make sure you are reaching your coverage goals before launching any feature,
1840or refactoring an existing one.
1841
1842### Single concept per test
1843
1844**Bad:**
1845
1846```javascript
1847import assert from "assert";
1848
1849describe("MomentJS", () => {
1850 it("handles date boundaries", () => {
1851 let date;
1852
1853 date = new MomentJS("1/1/2015");
1854 date.addDays(30);
1855 assert.equal("1/31/2015", date);
1856
1857 date = new MomentJS("2/1/2016");
1858 date.addDays(28);
1859 assert.equal("02/29/2016", date);
1860
1861 date = new MomentJS("2/1/2015");
1862 date.addDays(28);
1863 assert.equal("03/01/2015", date);
1864 });
1865});
1866```
1867
1868**Good:**
1869
1870```javascript
1871import assert from "assert";
1872
1873describe("MomentJS", () => {
1874 it("handles 30-day months", () => {
1875 const date = new MomentJS("1/1/2015");
1876 date.addDays(30);
1877 assert.equal("1/31/2015", date);
1878 });
1879
1880 it("handles leap year", () => {
1881 const date = new MomentJS("2/1/2016");
1882 date.addDays(28);
1883 assert.equal("02/29/2016", date);
1884 });
1885
1886 it("handles non-leap year", () => {
1887 const date = new MomentJS("2/1/2015");
1888 date.addDays(28);
1889 assert.equal("03/01/2015", date);
1890 });
1891});
1892```
1893
1894**[⬆ back to top](#table-of-contents)**
1895
1896## **Concurrency**
1897
1898### Use Promises, not callbacks
1899
1900Callbacks aren't clean, and they cause excessive amounts of nesting. With ES2015/ES6,
1901Promises are a built-in global type. Use them!
1902
1903**Bad:**
1904
1905```javascript
1906import { get } from "request";
1907import { writeFile } from "fs";
1908
1909get(
1910 "https://en.wikipedia.org/wiki/Robert_Cecil_Martin",
1911 (requestErr, response) => {
1912 if (requestErr) {
1913 console.error(requestErr);
1914 } else {
1915 writeFile("article.html", response.body, writeErr => {
1916 if (writeErr) {
1917 console.error(writeErr);
1918 } else {
1919 console.log("File written");
1920 }
1921 });
1922 }
1923 }
1924);
1925```
1926
1927**Good:**
1928
1929```javascript
1930import { get } from "request";
1931import { writeFile } from "fs";
1932
1933get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin")
1934 .then(response => {
1935 return writeFile("article.html", response);
1936 })
1937 .then(() => {
1938 console.log("File written");
1939 })
1940 .catch(err => {
1941 console.error(err);
1942 });
1943```
1944
1945**[⬆ back to top](#table-of-contents)**
1946
1947### Async/Await are even cleaner than Promises
1948
1949Promises are a very clean alternative to callbacks, but ES2017/ES8 brings async and await
1950which offer an even cleaner solution. All you need is a function that is prefixed
1951in an `async` keyword, and then you can write your logic imperatively without
1952a `then` chain of functions. Use this if you can take advantage of ES2017/ES8 features
1953today!
1954
1955**Bad:**
1956
1957```javascript
1958import { get } from "request-promise";
1959import { writeFile } from "fs-promise";
1960
1961get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin")
1962 .then(response => {
1963 return writeFile("article.html", response);
1964 })
1965 .then(() => {
1966 console.log("File written");
1967 })
1968 .catch(err => {
1969 console.error(err);
1970 });
1971```
1972
1973**Good:**
1974
1975```javascript
1976import { get } from "request-promise";
1977import { writeFile } from "fs-promise";
1978
1979async function getCleanCodeArticle() {
1980 try {
1981 const response = await get(
1982 "https://en.wikipedia.org/wiki/Robert_Cecil_Martin"
1983 );
1984 await writeFile("article.html", response);
1985 console.log("File written");
1986 } catch (err) {
1987 console.error(err);
1988 }
1989}
1990```
1991
1992**[⬆ back to top](#table-of-contents)**
1993
1994## **Error Handling**
1995
1996Thrown errors are a good thing! They mean the runtime has successfully
1997identified when something in your program has gone wrong and it's letting
1998you know by stopping function execution on the current stack, killing the
1999process (in Node), and notifying you in the console with a stack trace.
2000
2001### Don't ignore caught errors
2002
2003Doing nothing with a caught error doesn't give you the ability to ever fix
2004or react to said error. Logging the error to the console (`console.log`)
2005isn't much better as often times it can get lost in a sea of things printed
2006to the console. If you wrap any bit of code in a `try/catch` it means you
2007think an error may occur there and therefore you should have a plan,
2008or create a code path, for when it occurs.
2009
2010**Bad:**
2011
2012```javascript
2013try {
2014 functionThatMightThrow();
2015} catch (error) {
2016 console.log(error);
2017}
2018```
2019
2020**Good:**
2021
2022```javascript
2023try {
2024 functionThatMightThrow();
2025} catch (error) {
2026 // One option (more noisy than console.log):
2027 console.error(error);
2028 // Another option:
2029 notifyUserOfError(error);
2030 // Another option:
2031 reportErrorToService(error);
2032 // OR do all three!
2033}
2034```
2035
2036### Don't ignore rejected promises
2037
2038For the same reason you shouldn't ignore caught errors
2039from `try/catch`.
2040
2041**Bad:**
2042
2043```javascript
2044getdata()
2045 .then(data => {
2046 functionThatMightThrow(data);
2047 })
2048 .catch(error => {
2049 console.log(error);
2050 });
2051```
2052
2053**Good:**
2054
2055```javascript
2056getdata()
2057 .then(data => {
2058 functionThatMightThrow(data);
2059 })
2060 .catch(error => {
2061 // One option (more noisy than console.log):
2062 console.error(error);
2063 // Another option:
2064 notifyUserOfError(error);
2065 // Another option:
2066 reportErrorToService(error);
2067 // OR do all three!
2068 });
2069```
2070
2071**[⬆ back to top](#table-of-contents)**
2072
2073## **Formatting**
2074
2075Formatting is subjective. Like many rules herein, there is no hard and fast
2076rule that you must follow. The main point is DO NOT ARGUE over formatting.
2077There are [tons of tools](https://standardjs.com/rules.html) to automate this.
2078Use one! It's a waste of time and money for engineers to argue over formatting.
2079
2080For things that don't fall under the purview of automatic formatting
2081(indentation, tabs vs. spaces, double vs. single quotes, etc.) look here
2082for some guidance.
2083
2084### Use consistent capitalization
2085
2086JavaScript is untyped, so capitalization tells you a lot about your variables,
2087functions, etc. These rules are subjective, so your team can choose whatever
2088they want. The point is, no matter what you all choose, just be consistent.
2089
2090**Bad:**
2091
2092```javascript
2093const DAYS_IN_WEEK = 7;
2094const daysInMonth = 30;
2095
2096const songs = ["Back In Black", "Stairway to Heaven", "Hey Jude"];
2097const Artists = ["ACDC", "Led Zeppelin", "The Beatles"];
2098
2099function eraseDatabase() {}
2100function restore_database() {}
2101
2102class animal {}
2103class Alpaca {}
2104```
2105
2106**Good:**
2107
2108```javascript
2109const DAYS_IN_WEEK = 7;
2110const DAYS_IN_MONTH = 30;
2111
2112const SONGS = ["Back In Black", "Stairway to Heaven", "Hey Jude"];
2113const ARTISTS = ["ACDC", "Led Zeppelin", "The Beatles"];
2114
2115function eraseDatabase() {}
2116function restoreDatabase() {}
2117
2118class Animal {}
2119class Alpaca {}
2120```
2121
2122**[⬆ back to top](#table-of-contents)**
2123
2124### Function callers and callees should be close
2125
2126If a function calls another, keep those functions vertically close in the source
2127file. Ideally, keep the caller right above the callee. We tend to read code from
2128top-to-bottom, like a newspaper. Because of this, make your code read that way.
2129
2130**Bad:**
2131
2132```javascript
2133class PerformanceReview {
2134 constructor(employee) {
2135 this.employee = employee;
2136 }
2137
2138 lookupPeers() {
2139 return db.lookup(this.employee, "peers");
2140 }
2141
2142 lookupManager() {
2143 return db.lookup(this.employee, "manager");
2144 }
2145
2146 getPeerReviews() {
2147 const peers = this.lookupPeers();
2148 // ...
2149 }
2150
2151 perfReview() {
2152 this.getPeerReviews();
2153 this.getManagerReview();
2154 this.getSelfReview();
2155 }
2156
2157 getManagerReview() {
2158 const manager = this.lookupManager();
2159 }
2160
2161 getSelfReview() {
2162 // ...
2163 }
2164}
2165
2166const review = new PerformanceReview(employee);
2167review.perfReview();
2168```
2169
2170**Good:**
2171
2172```javascript
2173class PerformanceReview {
2174 constructor(employee) {
2175 this.employee = employee;
2176 }
2177
2178 perfReview() {
2179 this.getPeerReviews();
2180 this.getManagerReview();
2181 this.getSelfReview();
2182 }
2183
2184 getPeerReviews() {
2185 const peers = this.lookupPeers();
2186 // ...
2187 }
2188
2189 lookupPeers() {
2190 return db.lookup(this.employee, "peers");
2191 }
2192
2193 getManagerReview() {
2194 const manager = this.lookupManager();
2195 }
2196
2197 lookupManager() {
2198 return db.lookup(this.employee, "manager");
2199 }
2200
2201 getSelfReview() {
2202 // ...
2203 }
2204}
2205
2206const review = new PerformanceReview(employee);
2207review.perfReview();
2208```
2209
2210**[⬆ back to top](#table-of-contents)**
2211
2212## **Comments**
2213
2214### Only comment things that have business logic complexity.
2215
2216Comments are an apology, not a requirement. Good code _mostly_ documents itself.
2217
2218**Bad:**
2219
2220```javascript
2221function hashIt(data) {
2222 // The hash
2223 let hash = 0;
2224
2225 // Length of string
2226 const length = data.length;
2227
2228 // Loop through every character in data
2229 for (let i = 0; i < length; i++) {
2230 // Get character code.
2231 const char = data.charCodeAt(i);
2232 // Make the hash
2233 hash = (hash << 5) - hash + char;
2234 // Convert to 32-bit integer
2235 hash &= hash;
2236 }
2237}
2238```
2239
2240**Good:**
2241
2242```javascript
2243function hashIt(data) {
2244 let hash = 0;
2245 const length = data.length;
2246
2247 for (let i = 0; i < length; i++) {
2248 const char = data.charCodeAt(i);
2249 hash = (hash << 5) - hash + char;
2250
2251 // Convert to 32-bit integer
2252 hash &= hash;
2253 }
2254}
2255```
2256
2257**[⬆ back to top](#table-of-contents)**
2258
2259### Don't leave commented out code in your codebase
2260
2261Version control exists for a reason. Leave old code in your history.
2262
2263**Bad:**
2264
2265```javascript
2266doStuff();
2267// doOtherStuff();
2268// doSomeMoreStuff();
2269// doSoMuchStuff();
2270```
2271
2272**Good:**
2273
2274```javascript
2275doStuff();
2276```
2277
2278**[⬆ back to top](#table-of-contents)**
2279
2280### Don't have journal comments
2281
2282Remember, use version control! There's no need for dead code, commented code,
2283and especially journal comments. Use `git log` to get history!
2284
2285**Bad:**
2286
2287```javascript
2288/**
2289 * 2016-12-20: Removed monads, didn't understand them (RM)
2290 * 2016-10-01: Improved using special monads (JP)
2291 * 2016-02-03: Removed type-checking (LI)
2292 * 2015-03-14: Added combine with type-checking (JR)
2293 */
2294function combine(a, b) {
2295 return a + b;
2296}
2297```
2298
2299**Good:**
2300
2301```javascript
2302function combine(a, b) {
2303 return a + b;
2304}
2305```
2306
2307**[⬆ back to top](#table-of-contents)**
2308
2309### Avoid positional markers
2310
2311They usually just add noise. Let the functions and variable names along with the
2312proper indentation and formatting give the visual structure to your code.
2313
2314**Bad:**
2315
2316```javascript
2317////////////////////////////////////////////////////////////////////////////////
2318// Scope Model Instantiation
2319////////////////////////////////////////////////////////////////////////////////
2320$scope.model = {
2321 menu: "foo",
2322 nav: "bar"
2323};
2324
2325////////////////////////////////////////////////////////////////////////////////
2326// Action setup
2327////////////////////////////////////////////////////////////////////////////////
2328const actions = function() {
2329 // ...
2330};
2331```
2332
2333**Good:**
2334
2335```javascript
2336$scope.model = {
2337 menu: "foo",
2338 nav: "bar"
2339};
2340
2341const actions = function() {
2342 // ...
2343};
2344```
2345
2346**[⬆ back to top](#table-of-contents)**
2347
2348## Translation
2349
2350This is also available in other languages:
2351
2352- ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**:
2353 [GavBaros/clean-code-javascript-fr](https://github.com/GavBaros/clean-code-javascript-fr)
2354- ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [fesnt/clean-code-javascript](https://github.com/fesnt/clean-code-javascript)
2355- ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es)
2356- ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript)
2357- ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese**:
2358 - [alivebao/clean-code-js](https://github.com/alivebao/clean-code-js)
2359 - [beginor/clean-code-javascript](https://github.com/beginor/clean-code-javascript)
2360- ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [marcbruederlin/clean-code-javascript](https://github.com/marcbruederlin/clean-code-javascript)
2361- ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [qkraudghgh/clean-code-javascript-ko](https://github.com/qkraudghgh/clean-code-javascript-ko)
2362- ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [greg-dev/clean-code-javascript-pl](https://github.com/greg-dev/clean-code-javascript-pl)
2363- ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**:
2364 - [BoryaMogila/clean-code-javascript-ru/](https://github.com/BoryaMogila/clean-code-javascript-ru/)
2365 - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript)
2366- ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/)
2367- ![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/clean-code-javascript/](https://github.com/mitsuruog/clean-code-javascript/)
2368- ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesian**:
2369 [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/)
2370- ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**:
2371 [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/)
2372
2373**[⬆ back to top](#table-of-contents)**
2374