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