├── .editorconfig ├── .gitignore ├── README.md ├── favicon.png └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | insert_final_newline = false 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Windows ### 2 | Thumbs.db 3 | ehthumbs.db 4 | ehthumbs_vista.db 5 | Desktop.ini 6 | $RECYCLE.BIN/ 7 | 8 | *.cab 9 | *.msi 10 | *.msm 11 | *.msp 12 | *.lnk 13 | 14 | ### OSX ### 15 | *.DS_Store 16 | .AppleDouble 17 | .LSOverride 18 | 19 | # Icon must end with two \r 20 | Icon 21 | # Thumbnails 22 | ._* 23 | # Files that might appear in the root of a volume 24 | .DocumentRevisions-V100 25 | .fseventsd 26 | .Spotlight-V100 27 | .TemporaryItems 28 | .Trashes 29 | .VolumeIcon.icns 30 | .com.apple.timemachine.donotpresent 31 | # Directories potentially created on remote AFP share 32 | .AppleDB 33 | .AppleDesktop 34 | Network Trash Folder 35 | Temporary Items 36 | .apdisk 37 | 38 | node_modules 39 | npm-debug.log 40 | dist 41 | .idea 42 | *.min.js 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Repositório original: [ryanmcdermott/clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript) 2 | 3 | # codigo-limpo-javascript 4 | 5 | ## Índice 6 | 1. [Introdução](#introdução) 7 | 2. [Variáveis](#váriaveis) 8 | 3. [Funções](#funções) 9 | 4. [Objetos e estrutura dos dados](#objetos-e-estrutura-dos-dados) 10 | 5. [Classes](#classes) 11 | 6. [Testes](#testes) 12 | 7. [Concorrência e assincronia](#concorrência-e-assincronia) 13 | 8. [Tratamento de erros](#tratamento-de-erros) 14 | 9. [Formatação](#formatação) 15 | 10. [Comentários](#comentários) 16 | 17 | ## Introdução 18 | ![Imagem humorística sobre a estimativa de qualidade do código sendo o número de vezes que você xinga lendo um código](http://www.osnews.com/images/comics/wtfm.jpg) 19 | 20 | Princípios de Engenharia de Software, do livro de Robert C. Martin [*Código Limpo*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), adaptado para o Javascript. Isso não é um guia de estilo. É um guia para produzir código legível, reutilizável e software Javascript refatorável. 21 | 22 | Nem todo princípio aqui precisa ser seguido ao pé da letra, e alguns listados aqui não são universalmente aceitos. São somente guias e nada mais. Mas essas ideias se desenvolveram ao longo de anos de experiência em desenvolvimento pelos autores do livro *Código Limpo*. 23 | 24 | Nossa profissão de engenheiro de software tem pouco mais de 50 anos, e ainda estamos aprendendo muito. Quando arquitetura de software for tão velha quanto arquitetura em si é hoje, talvez teremos regras mais sólidas para seguir. Por agora, deixe essas diretrizes servirem como ponto de apoio para avaliar a qualidade do código que você e a sua equipe produz. 25 | 26 | Mais uma coisa: Entendendo essas regras não vai fazer de você um desenvolvedor melhor do dia pra noite, e aplicar essas regras por anos a fio não vai impedir que você cometa erros. Cada pedaço de código começa como um rascunho inicial, como argila fresca que vai ser moldada no seu formato final. Por último nós lapidamos as imperfeições quando revisamos o código com nosso time. Não se culpe por criar códigos iniciais que precisam de melhoria. 27 | 28 | ##**Váriaveis** 29 | ### Utilize nomes fáceis e com significado 30 | 31 | **Ruim:** 32 | ```javascript 33 | const yyyymmdstr = moment().format('YYYY/MM/DD'); 34 | ``` 35 | 36 | **Bom:** 37 | ```javascript 38 | const currentDate = moment().format('YYYY/MM/DD'); 39 | ``` 40 | **[⬆ voltar para o topo](#Índice)** 41 | 42 | ### Utilize o mesmo vocabulário quando variáveis tiverem relação (forem do mesmo tipo) 43 | 44 | **Ruim:** 45 | ```javascript 46 | getUserInfo(); 47 | getClientData(); 48 | getCustomerRecord(); 49 | ``` 50 | 51 | **Bom:** 52 | ```javascript 53 | getUser(); 54 | ``` 55 | **[⬆ voltar para o topo](#Índice)** 56 | 57 | ### Utilize nomes fáceis de se buscar 58 | Nós desenvolvedores iremos ler mais código do que escrever. É importante que o código que escrevemos seja legível e de fácil localização. Usando nomes difíceis para variáveis estaremos prejudicando a leitura de outros desenvolvedores. Ferramentas que podem ajudar nesse processo: 59 | 60 | [buddy.js](https://github.com/danielstjules/buddy.js) e 61 | [ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) 62 | 63 | **Ruim:** 64 | ```javascript 65 | // O que seria 86400000? Só quem escreveu que sabe 66 | setTimeout(blastOff, 86400000); 67 | 68 | ``` 69 | 70 | **Bom:** 71 | ```javascript 72 | // Declare esse número em maiúsculo como uma constante (const) 73 | const MILLISECONDS_IN_A_DAY = 86400000; 74 | 75 | setTimeout(blastOff, MILLISECONDS_IN_A_DAY); 76 | 77 | ``` 78 | **[⬆ voltar para o topo](#Índice)** 79 | 80 | ### Utilize variáveis auto-explicativas 81 | **Ruim:** 82 | ```javascript 83 | const address = 'One Infinite Loop, Cupertino 95014'; 84 | const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; 85 | saveCityZipCode(address.match(cityZipCodeRegex)[1], address.match(cityZipCodeRegex)[2]); 86 | ``` 87 | 88 | **Bom:** 89 | ```javascript 90 | const address = 'One Infinite Loop, Cupertino 95014'; 91 | const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; 92 | const [, city, zipCode] = address.match(cityZipCodeRegex) || []; 93 | saveCityZipCode(city, zipCode); 94 | ``` 95 | **[⬆ voltar para o topo](#Índice)** 96 | 97 | ### Escreva códigos descritivos 98 | Código explícito é mais fácil de ler que código implícito 99 | 100 | **Ruim:** 101 | ```javascript 102 | const locations = ['Austin', 'New York', 'San Francisco']; 103 | locations.forEach((l) => { 104 | doStuff(); 105 | doSomeOtherStuff(); 106 | // ... 107 | // ... 108 | // ... 109 | // Wait, what is `l` for again? 110 | dispatch(l); 111 | }); 112 | ``` 113 | 114 | **Bom:** 115 | ```javascript 116 | const locations = ['Austin', 'New York', 'San Francisco']; 117 | locations.forEach((location) => { 118 | doStuff(); 119 | doSomeOtherStuff(); 120 | // ... 121 | // ... 122 | // ... 123 | dispatch(location); 124 | }); 125 | ``` 126 | **[⬆ voltar para o topo](#Índice)** 127 | 128 | ### Não crie contextos redundantes 129 | Se sua classe ou objeto pai já descreve seu assunto, não repita o nome nas variáveis internas 130 | 131 | **Ruim:** 132 | ```javascript 133 | const Car = { 134 | carMake: 'Honda', 135 | carModel: 'Accord', 136 | carColor: 'Blue' 137 | }; 138 | 139 | function paintCar(car) { 140 | car.carColor = 'Red'; 141 | } 142 | ``` 143 | 144 | **Bom:** 145 | ```javascript 146 | const Car = { 147 | make: 'Honda', 148 | model: 'Accord', 149 | color: 'Blue' 150 | }; 151 | 152 | function paintCar(car) { 153 | car.color = 'Red'; 154 | } 155 | ``` 156 | **[⬆ voltar para o topo](#Índice)** 157 | 158 | ### Utilize parâmetros default nas funções, ao invés de condicionais de checagem 159 | Parâmetros predefinidos são mais limpos do que a criaçao de condicionais para checar se um argumento foi passado ou não por exemplo. Saiba que utilizando parâmetros predefinidos, sua função somente vai oferecer argumentos predefinidos para argumentos `undefined`. Outros valores falsos como `''`, `""`, `false`, `null`, `0`, e `NaN`, não serão substituídos por um valor predefinido. 160 | 161 | **Ruim:** 162 | ```javascript 163 | function createMicrobrewery(name) { 164 | const breweryName = name || 'Hipster Brew Co.'; 165 | // ... 166 | } 167 | 168 | ``` 169 | 170 | **Bom:** 171 | ```javascript 172 | function createMicrobrewery(breweryName = 'Hipster Brew Co.') { 173 | // ... 174 | } 175 | 176 | ``` 177 | **[⬆ voltar para o topo](#Índice)** 178 | 179 | ##**Funções** 180 | ### Parâmetros em uma função (2 ou menos é o ideal) 181 | Limitando a quantidade de parâmetros numa função é muito importante porque isso torna seu código mais fácil de ser testado. Geralmente quanto mais parâmetros existirem mais serão os casos de testes que terão que ser feitos com todas suas possíveis combinações. 182 | 183 | Um ou dois parâmetros é o cenário ideal, e três parâmetros devem ser evitados se possível. Qualquer função com mais de três argumentos deve ser repensada. Geralmente, se você tem mais de dois argumentos sua função está fazendo coisa demais. Caso não seja esse o case, é sempre possível inserir um objeto como parâmetro da função que dentro dele terão outras variáveis. 184 | 185 | Já que o Javascript permite a criação de objetos dinamicamente, você pode usar uma objeto em caso de precisar de muitos argumentos. 186 | 187 | Para tornar óbvio quais parâmetros uma função espera, você pode usar a sintaxe de [destructuring do ES6](http://www.jstips.co/en/use-destructuring-in-function-parameters/). Essa sintaxe tem algumas vantagens: 188 | 189 | 1. Quando alguém olha para sua função fica claro quais propriedades estão sendo usadas. 190 | 191 | 2. A técnica de destructuring também clona os valores primitivos que são passados como argumento para a função. Isso ajuda a prevenir efeitos colaterais que a sua função possa causar. Observação: Quando se utiliza destructuring em objetos e arrays eles não são clonados. 192 | 193 | 3. Linters podem acusar erros em propriedades que não são usadas quando se utiliza destructuring, o que não poderia ser possível antes. 194 | 195 | **Ruim:** 196 | ```javascript 197 | function createMenu(title, body, buttonText, cancellable) { 198 | // ... 199 | } 200 | ``` 201 | 202 | **Bom:** 203 | ```javascript 204 | function createMenu({ title, body, buttonText, cancellable }) { 205 | // ... 206 | } 207 | 208 | createMenu({ 209 | title: 'Foo', 210 | body: 'Bar', 211 | buttonText: 'Baz', 212 | cancellable: true 213 | }); 214 | ``` 215 | **[⬆ voltar para o topo](#Índice)** 216 | 217 | 218 | ### Funções devem fazer somente uma coisa 219 | 220 | Essa regra é a mais importante em engenharia de software. Quando funções fazem mais de uma coisa, ela são mais difíceis de serem reutilizadas, testadas e ficam mais complicadas de entender. Quando você isola uma função para somente uma ação, ela pode ser refatorada facilmente e o seu código será mais limpo. Se você tiver que absorver somente uma regra nesse guia, essa é a regra que você deve levar para toda a sua vida. Isso já vai deixá-lo a frente de muitos desenvolvedores. 221 | 222 | **Ruim:** 223 | ```javascript 224 | function emailClients(clients) { 225 | clients.forEach((client) => { 226 | const clientRecord = database.lookup(client); 227 | if (clientRecord.isActive()) { 228 | email(client); 229 | } 230 | }); 231 | } 232 | ``` 233 | 234 | **Bom:** 235 | ```javascript 236 | function emailClients(clients) { 237 | clients 238 | .filter(isClientActive) 239 | .forEach(email); 240 | } 241 | 242 | function isClientActive(client) { 243 | const clientRecord = database.lookup(client); 244 | return clientRecord.isActive(); 245 | } 246 | ``` 247 | **[⬆ voltar para o topo](#Índice)** 248 | 249 | 250 | ### Os nomes das funções devem dizer o que elas fazem 251 | 252 | **Ruim:** 253 | ```javascript 254 | function addToDate(date, month) { 255 | // ... 256 | } 257 | 258 | const date = new Date(); 259 | 260 | // Nesse caso abaixo, é difícil dizer pelo nome da função o que é adicionado 261 | addToDate(date, 1); 262 | ``` 263 | 264 | **Bom:** 265 | ```javascript 266 | function addMonthToDate(month, date) { 267 | // ... 268 | } 269 | 270 | const date = new Date(); 271 | addMonthToDate(1, date); 272 | ``` 273 | **[⬆ voltar para o topo](#Índice)** 274 | 275 | ### Funções devem ter somente um nível de abstração 276 | 277 | Geralmente, quando você tem mais de um nível de abstração, sua função está fazendo muita coisa sozinha. Dividir essa função em funções menores facilidade sua reutilização e é mais fácil de testar. 278 | 279 | **Ruim:** 280 | ```javascript 281 | function parseBetterJSAlternative(code) { 282 | const REGEXES = [ 283 | // ... 284 | ]; 285 | 286 | const statements = code.split(' '); 287 | const tokens = []; 288 | REGEXES.forEach((REGEX) => { 289 | statements.forEach((statement) => { 290 | // ... 291 | }); 292 | }); 293 | 294 | const ast = []; 295 | tokens.forEach((token) => { 296 | // lex... 297 | }); 298 | 299 | ast.forEach((node) => { 300 | // parse... 301 | }); 302 | } 303 | ``` 304 | 305 | **Bom:** 306 | ```javascript 307 | function tokenize(code) { 308 | const REGEXES = [ 309 | // ... 310 | ]; 311 | 312 | const statements = code.split(' '); 313 | const tokens = []; 314 | REGEXES.forEach((REGEX) => { 315 | statements.forEach((statement) => { 316 | tokens.push( /* ... */ ); 317 | }); 318 | }); 319 | 320 | return tokens; 321 | } 322 | 323 | function lexer(tokens) { 324 | const ast = []; 325 | tokens.forEach((token) => { 326 | ast.push( /* ... */ ); 327 | }); 328 | 329 | return ast; 330 | } 331 | 332 | function parseBetterJSAlternative(code) { 333 | const tokens = tokenize(code); 334 | const ast = lexer(tokens); 335 | ast.forEach((node) => { 336 | // parse... 337 | }); 338 | } 339 | ``` 340 | **[⬆ voltar para o topo](#Índice)** 341 | 342 | ### Remova código duplicado 343 | 344 | Faça o seu melhor para evitar código duplicado. Código duplicado é ruim porque significa que existe mais de um lugar onde você precisará atualizar alguma coisa caso alguma lógica mude. 345 | 346 | Imagine que você administra um restaurante e você deve controlar seu estoque: Todos seus tomates, cebolas, alho e pimentas, etc. 347 | Se você tem múltiplas listas que você deve observar, então cada vez que você serve um prato todas essas listas terão que ser atualizadas. Se você tiver somente uma lista, é só ela que você tem que atualizar! 348 | 349 | De vez em quando você tem código duplicado porque você tem duas ou três coisas que tem pequenas mudanças entre elas, 350 | mas que compartilham muito em comum, mas suas diferenças te forçam a ter duas ou três funções separadas que fazem quase a mesma coisa. 351 | Remover código duplicado nesse caso significa criar uma camada de abstração que possa lidar com essas pequenas diferenças com uma só função/módulo/classe. 352 | 353 | 354 | Executar essa abstração de forma correta é essencial, por isso deve-se seguir os princípios SOLID descritos na seção *Classes*. 355 | Abstrações ruins podem ser pior que código duplicado, então tome cuidado! Dito isso, se você pode fazer uma boa abstração, faça! 356 | Não se repita, caso contrário você vai se pegar atualizando código em múltiplos lugares toda vez que uma mudança tenha que ser feita. 357 | 358 | **Ruim:** 359 | ```javascript 360 | function showDeveloperList(developers) { 361 | developers.forEach((developer) => { 362 | const expectedSalary = developer.calculateExpectedSalary(); 363 | const experience = developer.getExperience(); 364 | const githubLink = developer.getGithubLink(); 365 | const data = { 366 | expectedSalary, 367 | experience, 368 | githubLink 369 | }; 370 | 371 | render(data); 372 | }); 373 | } 374 | 375 | function showManagerList(managers) { 376 | managers.forEach((manager) => { 377 | const expectedSalary = manager.calculateExpectedSalary(); 378 | const experience = manager.getExperience(); 379 | const portfolio = manager.getMBAProjects(); 380 | const data = { 381 | expectedSalary, 382 | experience, 383 | portfolio 384 | }; 385 | 386 | render(data); 387 | }); 388 | } 389 | ``` 390 | 391 | **Bom:** 392 | ```javascript 393 | function showEmployeeList(employees) { 394 | employees.forEach((employee) => { 395 | const expectedSalary = employee.calculateExpectedSalary(); 396 | const experience = employee.getExperience(); 397 | 398 | let portfolio = employee.getGithubLink(); 399 | 400 | if (employee.type === 'manager') { 401 | portfolio = employee.getMBAProjects(); 402 | } 403 | 404 | const data = { 405 | expectedSalary, 406 | experience, 407 | portfolio 408 | }; 409 | 410 | render(data); 411 | }); 412 | } 413 | ``` 414 | **[⬆ voltar para o topo](#Índice)** 415 | 416 | ### Crie objetos default com Object.assign() 417 | 418 | **Ruim:** 419 | ```javascript 420 | const menuConfig = { 421 | title: null, 422 | body: 'Bar', 423 | buttonText: null, 424 | cancellable: true 425 | }; 426 | 427 | function createMenu(config) { 428 | config.title = config.title || 'Foo'; 429 | config.body = config.body || 'Bar'; 430 | config.buttonText = config.buttonText || 'Baz'; 431 | config.cancellable = config.cancellable === undefined ? config.cancellable : true; 432 | } 433 | 434 | createMenu(menuConfig); 435 | ``` 436 | 437 | **Bom:** 438 | ```javascript 439 | const menuConfig = { 440 | title: 'Order', 441 | // Usuário não incluiu a chave 'body' 442 | buttonText: 'Send', 443 | cancellable: true 444 | }; 445 | 446 | function createMenu(config) { 447 | config = Object.assign({ 448 | title: 'Foo', 449 | body: 'Bar', 450 | buttonText: 'Baz', 451 | cancellable: true 452 | }, config); 453 | 454 | // config agora é: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} 455 | // ... 456 | } 457 | 458 | createMenu(menuConfig); 459 | ``` 460 | **[⬆ voltar para o topo](#Índice)** 461 | 462 | 463 | ### Não utilize flags como parâmetros de funções para sinalizar alguma condicional 464 | 465 | Flags automaticamente dizem que essa função faz mais de uma coisa. Funções devem fazer somente uma coisa. 466 | Divida suas funções caso ela esteja seguindo caminhos diferentes no código baseado em um cooleano como flag. 467 | 468 | **Ruim:** 469 | ```javascript 470 | function createFile(name, temp) { 471 | if (temp) { 472 | fs.create(`./temp/${name}`); 473 | } else { 474 | fs.create(name); 475 | } 476 | } 477 | ``` 478 | 479 | **Bom:** 480 | ```javascript 481 | function createFile(name) { 482 | fs.create(name); 483 | } 484 | 485 | function createTempFile(name) { 486 | createFile(`./temp/${name}`); 487 | } 488 | ``` 489 | **[⬆ voltar para o topo](#Índice)** 490 | 491 | ### Evite efeitos colaterais (parte 1) 492 | 493 | Uma função produz um efeito colateral caso ela fazer qualquer outra coisa do que pegar uma valor e retornar outro valor, ou valores. 494 | Um efeito colateral pode ser escrever em um arquivo, modificar uma variável global, ou acidentalmente enviar todo seu dinheiro para um estranho. 495 | 496 | Em certos momentos você vai precisar ter efeitos colaterais. Como no exemplo anterior, você pode precisar escrever em um arquivo. 497 | O que é necessário fazer é centralizar onde você vai realizar isso. Não tenha várias funções e classes que escrevem para um determinado arquivo. 498 | Tenha um único serviço centralizado para fazer isso. Um e somente um. 499 | 500 | O ponto central é evitar problemas comuns como compartilhar estados entre objetos sem nenhuma estrutura, 501 | usando tipos de dados mutáveis que podem ser escritos por qualquer coisa, e não centralizando onde você deseja que os efeitos colaterais devem ocorrer. 502 | Se você conseguir centralizar esse efeitos colaterais você será mais feliz que a maioria dos programadores. 503 | 504 | **Ruim:** 505 | ```javascript 506 | // Variável global referenciada pela função abaixo. 507 | // Se tivéssemos uma outra função que usasse esse nome, agora teríamos um array e o código poderia quebrar. 508 | let name = 'Ryan McDermott'; 509 | 510 | function splitIntoFirstAndLastName() { 511 | name = name.split(' '); 512 | } 513 | 514 | splitIntoFirstAndLastName(); 515 | 516 | console.log(name); // ['Ryan', 'McDermott']; 517 | ``` 518 | 519 | **Bom:** 520 | ```javascript 521 | function splitIntoFirstAndLastName(name) { 522 | return name.split(' '); 523 | } 524 | 525 | const name = 'Ryan McDermott'; 526 | const newName = splitIntoFirstAndLastName(name); 527 | 528 | console.log(name); // 'Ryan McDermott'; 529 | console.log(newName); // ['Ryan', 'McDermott']; 530 | ``` 531 | **[⬆ voltar para o topo](#Índice)** 532 | 533 | ### Evite efeitos colaterais (parte 2) 534 | 535 | Em Javascript, valores primitivos são passados como valor, e objetos e array são passados como referência. 536 | No caso de objetos e arrays, se a nossa função faz uma mudança em um array num carrinho de compras, por exemplo, adicionado um item para compra, 537 | então qualquer outra função que utilize esse array do 'carrinho' será afetada como essa adição. Isso pode ser ótimo, mas pode ser ruim também. 538 | 539 | Vamos imaginar uma situação ruim: 540 | 541 | O usuário clica em "Comprar", botão que chama uma função comprar() que inicia uma requisição e envia para o servidor um array chamado 'carrinho'. 542 | Caso a conexão a internet do usuário esteja ruim, a função comprar() teve de ficar tentando várias vezes executar a requisição ao servidor. 543 | 544 | Agora, e se nesse meio-tempo o usuário acidentalmente clicou no botão "Adicionar ao carrinho" em um item que ele na verdade não quer, 545 | antes da requisição ao servidor começar? 546 | 547 | Se isso acontece e a requisição ao servidor começa, então a função 'comprar' irá enviar o item escolhido acidentalmente, porque ele tem uma referência a um array 'carrinho' que outra função chamada 'adicionarAoCarrinho' modificou ao adicionar o item indesejado. 548 | 549 | Uma solução seria que a função 'adicionarAoCarrinho' sempre clonasse o array 'carrinho', editasse ele, e retornasse o clone. 550 | Isso iria garantir que nenhuma outra função que está guardando a referência do 'carrinho' seja afetada por uma mudança em outro lugar. 551 | 552 | Dois problemas com essa solução: 553 | 554 | 1. Existirão casos onde você de fato irá querer modificar esse objeto e não clonar ele, mas quando você adota essa prática de programação você irá perceber que esses casos são raros. A maioria das coisas podem ser refatoras para evitar efeitos colaterais! 555 | 556 | 2. Clonar objetos muito grandes pode ser peado em termos de performance. 557 | Por sorte isso não é um grande problema na prática porque existem [boas bibliotecas](https://facebook.github.io/immutable-js/) 558 | que permitem que essa abordagem de programação seja rápida e não seja tão intensiva em questão de memória como seria caso se clonasse objetos e arrays grandes manualmente. 559 | 560 | **Ruim:** 561 | ```javascript 562 | const adicionarAoCarrinho = (cart, item) => { 563 | cart.push({ item, date: Date.now() }); 564 | }; 565 | ``` 566 | 567 | **Bom:** 568 | ```javascript 569 | const adicionarAoCarrinho = (cart, item) => { 570 | return [...cart, { item, date : Date.now() }]; 571 | }; 572 | ``` 573 | 574 | **[⬆ voltar para o topo](#Índice)** 575 | 576 | ### Não escreva funções globais 577 | 578 | Poluir o escopo global é uma prática ruim no Javascript, porque você pode causar conflito com alguma outra biblioteca 579 | e o usuário da sua API ou do seu código pode acabar com um erro em produção. 580 | 581 | Um exemplo, e se você quisesse estender o objeto nativo Array para ter um método 'diff' que poderia mostrar a diferença entre duas arrays? 582 | Você poderia escrever sua nova função para o 'Array.prototype', mas isso poderia conflitar com outra biblioteca que faz o mesmo. 583 | 584 | E se outra biblioteca estiver usando o termo 'diff' para achar a diferença entre o primeiro e o último elemento da array? 585 | É por isso que seria bem melhor somente usar classes ES2015/ES6 e simplesmente estender o 'Array' global. 586 | 587 | **Ruim:** 588 | ```javascript 589 | Array.prototype.diff = function diff(comparisonArray) { 590 | const hash = new Set(comparisonArray); 591 | return this.filter(elem => !hash.has(elem)); 592 | }; 593 | ``` 594 | 595 | **Bom:** 596 | ```javascript 597 | class SuperArray extends Array { 598 | diff(comparisonArray) { 599 | const hash = new Set(comparisonArray); 600 | return this.filter(elem => !hash.has(elem)); 601 | } 602 | } 603 | ``` 604 | **[⬆ voltar para o topo](#Índice)** 605 | 606 | ### Dê preferência para a programação funcional ao invés da imperativa 607 | Javascript não é uma linguagem funcional do jeito que o Haskell é, mas ainda assim tem seu toque funcional. 608 | Linguagens funcionais são mais limpas e mais fáceis de testar. Favoreça esse estilo de programação quando puder. 609 | 610 | **Ruim:** 611 | ```javascript 612 | const programmerOutput = [ 613 | { 614 | name: 'Uncle Bobby', 615 | linesOfCode: 500 616 | }, { 617 | name: 'Suzie Q', 618 | linesOfCode: 1500 619 | }, { 620 | name: 'Jimmy Gosling', 621 | linesOfCode: 150 622 | }, { 623 | name: 'Gracie Hopper', 624 | linesOfCode: 1000 625 | } 626 | ]; 627 | 628 | let totalOutput = 0; 629 | 630 | for (let i = 0; i < programmerOutput.length; i++) { 631 | totalOutput += programmerOutput[i].linesOfCode; 632 | } 633 | ``` 634 | 635 | **Bom:** 636 | ```javascript 637 | const programmerOutput = [ 638 | { 639 | name: 'Uncle Bobby', 640 | linesOfCode: 500 641 | }, { 642 | name: 'Suzie Q', 643 | linesOfCode: 1500 644 | }, { 645 | name: 'Jimmy Gosling', 646 | linesOfCode: 150 647 | }, { 648 | name: 'Gracie Hopper', 649 | linesOfCode: 1000 650 | } 651 | ]; 652 | 653 | const INITIAL_VALUE = 0; 654 | 655 | const totalOutput = programmerOutput 656 | .map((programmer) => programmer.linesOfCode) 657 | .reduce((acc, linesOfCode) => acc + linesOfCode, INITIAL_VALUE); 658 | ``` 659 | **[⬆ voltar para o topo](#Índice)** 660 | 661 | 662 | ### Encapsule suas condicionais 663 | 664 | **Ruim:** 665 | ```javascript 666 | if (fsm.state === 'fetching' && isEmpty(listNode)) { 667 | // ... 668 | } 669 | ``` 670 | 671 | **Bom:** 672 | ```javascript 673 | function shouldShowSpinner(fsm, listNode) { 674 | return fsm.state === 'fetching' && isEmpty(listNode); 675 | } 676 | 677 | if (shouldShowSpinner(fsmInstance, listNodeInstance)) { 678 | // ... 679 | } 680 | ``` 681 | **[⬆ voltar para o topo](#Índice)** 682 | 683 | ### Evite condicionais negativas 684 | 685 | **Ruim:** 686 | ```javascript 687 | function isDOMNodeNotPresent(node) { 688 | // ... 689 | } 690 | 691 | if (!isDOMNodeNotPresent(node)) { 692 | // ... 693 | } 694 | ``` 695 | 696 | **Bom:** 697 | ```javascript 698 | function isDOMNodePresent(node) { 699 | // ... 700 | } 701 | 702 | if (isDOMNodePresent(node)) { 703 | // ... 704 | } 705 | ``` 706 | **[⬆ voltar para o topo](#Índice)** 707 | 708 | 709 | ### Evite condicionais 710 | Isso parece uma tarefa impossível. Quando se escuta isso pela primeira vez, a maioria das pessoas se pergunta, 711 | "Como eu vou poder fazer qualquer coisa sem um if ou else?". A resposta é que você pode utilizar polimorfismo, ou seja, criando classes diferentes, derivadas de uma mesma classe pai, com comportamentos diferentes definidos em cada uma dessas classes. 712 | A segunda pergunta é geralmente, "ok, porque eu deveria fazer isso?". A resposta vem de um princípio que foi dito antes. 713 | Uma função deve fazer somente uma coisa. Quando você tem classes e funções que tem 'if', você está dizendo que ao usuário que sua função faz mais de uma coisa. 714 | Lembre-se, faça somente uma coisa. 715 | 716 | **Ruim:** 717 | ```javascript 718 | class Airplane { 719 | // ... 720 | getCruisingAltitude() { 721 | switch (this.type) { 722 | case '777': 723 | return this.getMaxAltitude() - this.getPassengerCount(); 724 | case 'Air Force One': 725 | return this.getMaxAltitude(); 726 | case 'Cessna': 727 | return this.getMaxAltitude() - this.getFuelExpenditure(); 728 | } 729 | } 730 | } 731 | ``` 732 | 733 | **Bom:** 734 | ```javascript 735 | class Airplane { 736 | // ... 737 | } 738 | 739 | class Boeing777 extends Airplane { 740 | // ... 741 | getCruisingAltitude() { 742 | return this.getMaxAltitude() - this.getPassengerCount(); 743 | } 744 | } 745 | 746 | class AirForceOne extends Airplane { 747 | // ... 748 | getCruisingAltitude() { 749 | return this.getMaxAltitude(); 750 | } 751 | } 752 | 753 | class Cessna extends Airplane { 754 | // ... 755 | getCruisingAltitude() { 756 | return this.getMaxAltitude() - this.getFuelExpenditure(); 757 | } 758 | } 759 | ``` 760 | **[⬆ voltar para o topo](#Índice)** 761 | 762 | 763 | ### Evite checagem de tipo (parte 1) 764 | Javascript não é tipificado, o que significa que sua função pode aceitar qualquer tipo de argumento. 765 | Às vezes essa liberdade pode ser ruim e você pode ficar tentado a escrever uma checagem de tipo ns suas funções. 766 | Existem várias maneiras diferentes para evitar que você tenha que fazer essa checagem. 767 | A primeira coisa a considerar são API consistentes. 768 | 769 | **Ruim:** 770 | ```javascript 771 | function travelToTexas(vehicle) { 772 | if (vehicle instanceof Bicycle) { 773 | vehicle.pedal(this.currentLocation, new Location('texas')); 774 | } else if (vehicle instanceof Car) { 775 | vehicle.drive(this.currentLocation, new Location('texas')); 776 | } 777 | } 778 | ``` 779 | 780 | **Bom:** 781 | ```javascript 782 | function travelToTexas(vehicle) { 783 | vehicle.move(this.currentLocation, new Location('texas')); 784 | } 785 | ``` 786 | **[⬆ voltar para o topo](#Índice)** 787 | 788 | 789 | ### Evite checagem de tipo (parte 2) 790 | Se você está trabalhando com valores primitivos como strings e números você não pode usar polimorfismo 791 | mas você sentirá a necessidade de checar o tipo dos valores, você deve considerar usar Typescript. 792 | É uma excelente alternativa ao Javascript normal, porque provê tipagem em cima da sintaxe normal do Javascript. 793 | O problema em fazer a checagem de tipos manualmente como Javascript normal é que fazer de uma maneira correta obriga você a escrever muito código extra 794 | fazer você perder a legibilidade da lógica que realmente interessa. 795 | Mantenha seu Javascript limpo, escreva bons testes, e tenha boas revisões de código. Caso contrário, faça tudo isso com Typescript (que como foi dito, é uma boa alternativa). 796 | 797 | **Ruim:** 798 | ```javascript 799 | function combine(val1, val2) { 800 | if (typeof val1 === 'number' && typeof val2 === 'number' || 801 | typeof val1 === 'string' && typeof val2 === 'string') { 802 | return val1 + val2; 803 | } 804 | 805 | throw new Error('Must be of type String or Number'); 806 | } 807 | ``` 808 | 809 | **Bom:** 810 | ```javascript 811 | function combine(val1, val2) { 812 | return val1 + val2; 813 | } 814 | ``` 815 | **[⬆ voltar para o topo](#Índice)** 816 | 817 | ### Não otimize demais 818 | Browsers modernos fazem várias otimizações por debaixo dos panos em tempo de execução. 819 | Muitas vezes, se você está otimizando, você está apenas perdendo seu tempo. [Existem boas fontes](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) para checar 820 | onde otimização ainda pode ser feita. Tenha esse pontos como seu foco, até que sejam sanados, caso sejam. 821 | 822 | **Ruim:** 823 | ```javascript 824 | 825 | // Em browsers antigos, cada iteração com `list.length` não cacheado será custoso 826 | // porque `list.length` será recomputado. Em browsers modernos, isso já é otimizado. 827 | for (let i = 0, len = list.length; i < len; i++) { 828 | // ... 829 | } 830 | ``` 831 | 832 | **Bom:** 833 | ```javascript 834 | for (let i = 0; i < list.length; i++) { 835 | // ... 836 | } 837 | ``` 838 | **[⬆ voltar para o topo](#Índice)** 839 | 840 | ### Remova código morto 841 | Código morto é tão ruim quanto código duplicado. Não existe razão para tê-lo em sua base de código. 842 | Se não está sendo chamado, remova ele! Ainda poderá ser resgatado no seu versionamento caso você ainda precise dele. 843 | 844 | **Ruim:** 845 | ```javascript 846 | function oldRequestModule(url) { 847 | // ... 848 | } 849 | 850 | function newRequestModule(url) { 851 | // ... 852 | } 853 | 854 | const req = newRequestModule; 855 | inventoryTracker('apples', req, 'www.inventory-awesome.io'); 856 | 857 | ``` 858 | 859 | **Bom:** 860 | ```javascript 861 | function newRequestModule(url) { 862 | // ... 863 | } 864 | 865 | const req = newRequestModule; 866 | inventoryTracker('apples', req, 'www.inventory-awesome.io'); 867 | ``` 868 | **[⬆ voltar para o topo](#Índice)** 869 | 870 | ##**Objetos e estrutura dos dados** 871 | ### Utilize getters e setters 872 | 873 | Utilizando getters e setters para acessar dados em objetos pode ser melhor do que simplesmente olhar pela propriedade em um objeto. 874 | "Porque?", você pode perguntar. 875 | Bem, aqui está uma lista não ordenada de motivos: 876 | 877 | * Quando você quer fazer mais do que simplesmente pegar uma propriedade do objeto, você não tem que olhar e mudar todo mundo que acessar aquele objeto 878 | * Adicionar uma validação fica fácil ao dar um 'set' 879 | * Encapsula a representação interna do objeto 880 | * Fica fácil adicionar log e tratamento de erros ao dar get and set 881 | * Você pode fazer lazy loading com as propriedades do seu objeto, como é o caso de pegar essa propriedade no servidor 882 | 883 | **Ruim:** 884 | ```javascript 885 | function makeBankAccount() { 886 | // ... 887 | 888 | return { 889 | balance: 0, 890 | // ... 891 | }; 892 | } 893 | 894 | const account = makeBankAccount(); 895 | account.balance = 100; 896 | ``` 897 | 898 | **Bom:** 899 | ```javascript 900 | function makeBankAccount() { 901 | // Propriedade abaixo é privada 902 | let balance = 0; 903 | 904 | // Um 'getter', tornado público através to objeto retornado abaixo 905 | function getBalance() { 906 | return balance; 907 | } 908 | 909 | // Um 'setter', tornado público através to objeto retornado abaixo 910 | function setBalance(amount) { 911 | // é possível validar antes de atualizar a propriedade 'balance' 912 | balance = amount; 913 | } 914 | 915 | return { 916 | // ... 917 | getBalance, 918 | setBalance, 919 | }; 920 | } 921 | 922 | const account = makeBankAccount(); 923 | account.setBalance(100); 924 | ``` 925 | **[⬆ voltar para o topo](#Índice)** 926 | 927 | ### Faça seus objetos terem membros privados 928 | Isso pode ser feito através de closures (para ES5 e abaixo). 929 | 930 | **Ruim:** 931 | ```javascript 932 | 933 | const Employee = function(name) { 934 | this.name = name; 935 | }; 936 | 937 | Employee.prototype.getName = function getName() { 938 | return this.name; 939 | }; 940 | 941 | const employee = new Employee('John Doe'); 942 | console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe 943 | delete employee.name; 944 | console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined 945 | ``` 946 | 947 | **Bom:** 948 | ```javascript 949 | function makeEmployee(name) { 950 | return { 951 | getName() { 952 | return name; 953 | }, 954 | }; 955 | } 956 | 957 | const employee = makeEmployee('John Doe'); 958 | console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe 959 | delete employee.name; 960 | console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe 961 | ``` 962 | **[⬆ voltar para o topo](#Índice)** 963 | 964 | 965 | ##**Classes** 966 | 967 | ### Princípio de uma só responsabilidade 968 | 969 | Como dito no livro Código Limpo, "Nunca deve existir mais de uma razão para uma classe mudar". 970 | É tentador embutir muita funcionalidade em uma classe só, como embutir tudo em uma mala quando só se pode levar uma mala no avião. 971 | O problema com isso é que a sua classe não será coesa conceitualmente e isso vai te dar muitas razões para mudar a classe toda hora. 972 | Minimizar as vezes que você precisa mudar uma classe é importante. 973 | É importante porque se muita funcionalidade está em uma única classe, e você modifica uma parte dela, pode ser difícil de entender como isso vai afetar outros módulos que dependem dela. 974 | 975 | 976 | **Ruim:** 977 | ```javascript 978 | class UserSettings { 979 | constructor(user) { 980 | this.user = user; 981 | } 982 | 983 | changeSettings(settings) { 984 | if (this.verifyCredentials()) { 985 | // ... 986 | } 987 | } 988 | 989 | verifyCredentials() { 990 | // ... 991 | } 992 | } 993 | ``` 994 | 995 | **Bom:** 996 | ```javascript 997 | class UserAuth { 998 | constructor(user) { 999 | this.user = user; 1000 | } 1001 | 1002 | verifyCredentials() { 1003 | // ... 1004 | } 1005 | } 1006 | 1007 | 1008 | class UserSettings { 1009 | constructor(user) { 1010 | this.user = user; 1011 | this.auth = new UserAuth(user); 1012 | } 1013 | 1014 | changeSettings(settings) { 1015 | if (this.auth.verifyCredentials()) { 1016 | // ... 1017 | } 1018 | } 1019 | } 1020 | ``` 1021 | **[⬆ voltar para o topo](#Índice)** 1022 | 1023 | ### Princípio Aberto/Fechado 1024 | 1025 | Como foi dito por Bertrand Meyer, "entidades de software (classes, módulos, funções, etc.) devem estar abertas para extensões, mas fechadas para modificação." 1026 | O que isso significa? Esse princípio basicamente diz que você deve permitir que usuários adicionem novas funcionalidades sem poder alterar código existente. 1027 | 1028 | **Ruim:** 1029 | ```javascript 1030 | class AjaxAdapter extends Adapter { 1031 | constructor() { 1032 | super(); 1033 | this.name = 'ajaxAdapter'; 1034 | } 1035 | } 1036 | 1037 | class NodeAdapter extends Adapter { 1038 | constructor() { 1039 | super(); 1040 | this.name = 'nodeAdapter'; 1041 | } 1042 | } 1043 | 1044 | class HttpRequester { 1045 | constructor(adapter) { 1046 | this.adapter = adapter; 1047 | } 1048 | 1049 | fetch(url) { 1050 | if (this.adapter.name === 'ajaxAdapter') { 1051 | return makeAjaxCall(url).then((response) => { 1052 | // transform response and return 1053 | }); 1054 | } else if (this.adapter.name === 'httpNodeAdapter') { 1055 | return makeHttpCall(url).then((response) => { 1056 | // transform response and return 1057 | }); 1058 | } 1059 | } 1060 | } 1061 | 1062 | function makeAjaxCall(url) { 1063 | // request and return promise 1064 | } 1065 | 1066 | function makeHttpCall(url) { 1067 | // request and return promise 1068 | } 1069 | ``` 1070 | 1071 | **Bom:** 1072 | ```javascript 1073 | class AjaxAdapter extends Adapter { 1074 | constructor() { 1075 | super(); 1076 | this.name = 'ajaxAdapter'; 1077 | } 1078 | 1079 | request(url) { 1080 | // request and return promise 1081 | } 1082 | } 1083 | 1084 | class NodeAdapter extends Adapter { 1085 | constructor() { 1086 | super(); 1087 | this.name = 'nodeAdapter'; 1088 | } 1089 | 1090 | request(url) { 1091 | // request and return promise 1092 | } 1093 | } 1094 | 1095 | class HttpRequester { 1096 | constructor(adapter) { 1097 | this.adapter = adapter; 1098 | } 1099 | 1100 | fetch(url) { 1101 | return this.adapter.request(url).then((response) => { 1102 | // transform response and return 1103 | }); 1104 | } 1105 | } 1106 | ``` 1107 | **[⬆ voltar para o topo](#Índice)** 1108 | 1109 | 1110 | ### Princípio da substituição de Liskov 1111 | 1112 | Este é um termo que assusta mas o conceito é bem simples. 1113 | É formalmente definido como "Se S é um subtipo de T, então objetos do tipo T podem ser substituídos com objetos do tipo S 1114 | (objetos do tipo S podem substituir objetos do tipo T) sem alterar qualquer propriedade do programa (exatidão, tarefa executada, etc.)" 1115 | Essa definição assusta ainda mais. 1116 | 1117 | A melhor explicação para esse princípio é, se você tem uma classe pai e uma classe filho, 1118 | então a classe pai ou a classe filho podem ser usadas sem obter resultados incorretos. Isso ainda pode parecer confuso, então vamos olhar o clássico exemplo do Retângulo-Quadrado. 1119 | 1120 | Matematicamente, um quadrado é um retângulo, mas se o seu modelo está usando a relação "é um" via hereditariedade, você pode ter problemas. 1121 | 1122 | **Ruim:** 1123 | ```javascript 1124 | class Rectangle { 1125 | constructor() { 1126 | this.width = 0; 1127 | this.height = 0; 1128 | } 1129 | 1130 | setColor(color) { 1131 | // ... 1132 | } 1133 | 1134 | render(area) { 1135 | // ... 1136 | } 1137 | 1138 | setWidth(width) { 1139 | this.width = width; 1140 | } 1141 | 1142 | setHeight(height) { 1143 | this.height = height; 1144 | } 1145 | 1146 | getArea() { 1147 | return this.width * this.height; 1148 | } 1149 | } 1150 | 1151 | class Square extends Rectangle { 1152 | setWidth(width) { 1153 | this.width = width; 1154 | this.height = width; 1155 | } 1156 | 1157 | setHeight(height) { 1158 | this.width = height; 1159 | this.height = height; 1160 | } 1161 | } 1162 | 1163 | function renderLargeRectangles(rectangles) { 1164 | rectangles.forEach((rectangle) => { 1165 | rectangle.setWidth(4); 1166 | rectangle.setHeight(5); 1167 | const area = rectangle.getArea(); // RUIM: Vai retornar 25 para quadrado, deveria ser 20 1168 | rectangle.render(area); 1169 | }); 1170 | } 1171 | 1172 | const rectangles = [new Rectangle(), new Rectangle(), new Square()]; 1173 | renderLargeRectangles(rectangles); 1174 | ``` 1175 | 1176 | **Bom:** 1177 | ```javascript 1178 | class Shape { 1179 | setColor(color) { 1180 | // ... 1181 | } 1182 | 1183 | render(area) { 1184 | // ... 1185 | } 1186 | } 1187 | 1188 | class Rectangle extends Shape { 1189 | constructor(width, height) { 1190 | super(); 1191 | this.width = width; 1192 | this.height = height; 1193 | } 1194 | 1195 | getArea() { 1196 | return this.width * this.height; 1197 | } 1198 | } 1199 | 1200 | class Square extends Shape { 1201 | constructor(length) { 1202 | super(); 1203 | this.length = length; 1204 | } 1205 | 1206 | getArea() { 1207 | return this.length * this.length; 1208 | } 1209 | } 1210 | 1211 | function renderLargeShapes(shapes) { 1212 | shapes.forEach((shape) => { 1213 | const area = shape.getArea(); 1214 | shape.render(area); 1215 | }); 1216 | } 1217 | 1218 | const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; 1219 | renderLargeShapes(shapes); 1220 | ``` 1221 | **[⬆ voltar para o topo](#Índice)** 1222 | 1223 | ### Princípio de segregação de interface 1224 | Javascript não tem interfaces, então esse princípio não se aplica tão estritamente quanto outras linguagens que tem interface implementada. 1225 | Entretanto, é importante e relevante até mesmo por causa da falta de tipagem do Javascript. 1226 | 1227 | Esse princípio diz que "Clientes não devem ser forçados a depender de interfaces que eles não usam." 1228 | Interfaces são contratos implícitos em Javascript por causa do Duck Typing (Duck typing é um termo usado para se referir a linguagens dinâmicas que não tem tipagem forte) 1229 | 1230 | Um bom exemplo para olhar isso e que exemplifica esse princípio é para classes que requerem objetos com configuração grande. 1231 | 1232 | Não obbrigar a seus clientes a configurar muitas opçòes é benéfico, porque na maioria do tempo eles não vão precisar de todas as configurações. 1233 | Tornando elas opcionais ajuda a prevenir uma interface muito gorda. 1234 | 1235 | **Ruim:** 1236 | ```javascript 1237 | class DOMTraverser { 1238 | constructor(settings) { 1239 | this.settings = settings; 1240 | this.setup(); 1241 | } 1242 | 1243 | setup() { 1244 | this.rootNode = this.settings.rootNode; 1245 | this.animationModule.setup(); 1246 | } 1247 | 1248 | traverse() { 1249 | // ... 1250 | } 1251 | } 1252 | 1253 | const $ = new DOMTraverser({ 1254 | rootNode: document.getElementsByTagName('body'), 1255 | animationModule() {} // Na maioria do tempo, nós não vamos precisar animar enquando se executa essa função 1256 | // ... 1257 | }); 1258 | 1259 | ``` 1260 | 1261 | **Bom:** 1262 | ```javascript 1263 | class DOMTraverser { 1264 | constructor(settings) { 1265 | this.settings = settings; 1266 | this.options = settings.options; 1267 | this.setup(); 1268 | } 1269 | 1270 | setup() { 1271 | this.rootNode = this.settings.rootNode; 1272 | this.setupOptions(); 1273 | } 1274 | 1275 | setupOptions() { 1276 | if (this.options.animationModule) { 1277 | // ... 1278 | } 1279 | } 1280 | 1281 | traverse() { 1282 | // ... 1283 | } 1284 | } 1285 | 1286 | const $ = new DOMTraverser({ 1287 | rootNode: document.getElementsByTagName('body'), 1288 | options: { 1289 | animationModule() {} 1290 | } 1291 | }); 1292 | ``` 1293 | **[⬆ voltar para o topo](#Índice)** 1294 | 1295 | 1296 | ### Princípio da inversão de dependências 1297 | 1298 | Esse princípio diz 2 coisas essenciais: 1299 | 1300 | 1. Módulos de alto nível não devem depender de módulos baixo-nível. Os dois devem depender de abstrações. 1301 | 1302 | 2. Abstrações não devem depender de detalhes. Detalhes devem depender de abstrações 1303 | 1304 | 1305 | Isso pode ser difícil à primeira vista, mas se você já trabalhou com Angular.js, você já viu uma implementação desse princípio na forma de 1306 | Injeção de dependências (DI). Enquanto eles não são conceitos idênticos, o princípio da inversão de dependências previne módulos alto-nível 1307 | de saberem os detalhes dos módulos baixo-nível e de mexerem neles. Isso pode ser alcançado através da injeção de dependências. 1308 | Um grande benefício disso é que isso reduz a interdepência entre módulos. Interdepência é um padrão muito ruim porque torna seu código mais difícil de ser refatorado. 1309 | 1310 | Como dito anteriormente, Javascript não tem interfaces, então as abstrações que das quais se depende são contratos implícitos. 1311 | No caso, os métodos e propriedades que um objeto/classe expõe para outro objeto/classe. 1312 | No exemplo abaixo, o contrato implícito é que qualquer módulo Request para um `InventoryTracker` vai ter um método `requestItems`. 1313 | 1314 | **Ruim:** 1315 | ```javascript 1316 | class InventoryRequester { 1317 | constructor() { 1318 | this.REQ_METHODS = ['HTTP']; 1319 | } 1320 | 1321 | requestItem(item) { 1322 | // ... 1323 | } 1324 | } 1325 | 1326 | class InventoryTracker { 1327 | constructor(items) { 1328 | this.items = items; 1329 | 1330 | // RUIM: Nós criamos uma dependência em uma implementação de request específica 1331 | // Nós devemos ter somente requestItems dependendo de um método request: `request` 1332 | this.requester = new InventoryRequester(); 1333 | } 1334 | 1335 | requestItems() { 1336 | this.items.forEach((item) => { 1337 | this.requester.requestItem(item); 1338 | }); 1339 | } 1340 | } 1341 | 1342 | const inventoryTracker = new InventoryTracker(['apples', 'bananas']); 1343 | inventoryTracker.requestItems(); 1344 | ``` 1345 | 1346 | **Bom:** 1347 | ```javascript 1348 | class InventoryTracker { 1349 | constructor(items, requester) { 1350 | this.items = items; 1351 | this.requester = requester; 1352 | } 1353 | 1354 | requestItems() { 1355 | this.items.forEach((item) => { 1356 | this.requester.requestItem(item); 1357 | }); 1358 | } 1359 | } 1360 | 1361 | class InventoryRequesterV1 { 1362 | constructor() { 1363 | this.REQ_METHODS = ['HTTP']; 1364 | } 1365 | 1366 | requestItem(item) { 1367 | // ... 1368 | } 1369 | } 1370 | 1371 | class InventoryRequesterV2 { 1372 | constructor() { 1373 | this.REQ_METHODS = ['WS']; 1374 | } 1375 | 1376 | requestItem(item) { 1377 | // ... 1378 | } 1379 | } 1380 | 1381 | // Ao construir nossa dependências externamente e injetando elas, nós podemos facilmente 1382 | // substituir nosso módulo de request por um que utiliza websocket por exemplo. 1383 | const inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterV2()); 1384 | inventoryTracker.requestItems(); 1385 | ``` 1386 | **[⬆ voltar para o topo](#Índice)** 1387 | 1388 | 1389 | ### Prefira classes ES2015/ES6 ao invés de simples funções ES5 1390 | É bem difícil de ter herança de classes, construtores, e definições de métodos bem legíveis para 'classes' em ES5. 1391 | Caso você necessite herança (e veja que você pode não precisar), então prefira classes. Entretanto, prefira funções pequenas ao invés de classes até você 1392 | sentir a necessidade de objetos maiores e mais complexos. 1393 | 1394 | **Ruim:** 1395 | ```javascript 1396 | const Animal = function(age) { 1397 | if (!(this instanceof Animal)) { 1398 | throw new Error('Instantiate Animal with `new`'); 1399 | } 1400 | 1401 | this.age = age; 1402 | }; 1403 | 1404 | Animal.prototype.move = function move() {}; 1405 | 1406 | const Mammal = function(age, furColor) { 1407 | if (!(this instanceof Mammal)) { 1408 | throw new Error('Instantiate Mammal with `new`'); 1409 | } 1410 | 1411 | Animal.call(this, age); 1412 | this.furColor = furColor; 1413 | }; 1414 | 1415 | Mammal.prototype = Object.create(Animal.prototype); 1416 | Mammal.prototype.constructor = Mammal; 1417 | Mammal.prototype.liveBirth = function liveBirth() {}; 1418 | 1419 | const Human = function(age, furColor, languageSpoken) { 1420 | if (!(this instanceof Human)) { 1421 | throw new Error('Instantiate Human with `new`'); 1422 | } 1423 | 1424 | Mammal.call(this, age, furColor); 1425 | this.languageSpoken = languageSpoken; 1426 | }; 1427 | 1428 | Human.prototype = Object.create(Mammal.prototype); 1429 | Human.prototype.constructor = Human; 1430 | Human.prototype.speak = function speak() {}; 1431 | ``` 1432 | 1433 | **Bom:** 1434 | ```javascript 1435 | class Animal { 1436 | constructor(age) { 1437 | this.age = age; 1438 | } 1439 | 1440 | move() { /* ... */ } 1441 | } 1442 | 1443 | class Mammal extends Animal { 1444 | constructor(age, furColor) { 1445 | super(age); 1446 | this.furColor = furColor; 1447 | } 1448 | 1449 | liveBirth() { /* ... */ } 1450 | } 1451 | 1452 | class Human extends Mammal { 1453 | constructor(age, furColor, languageSpoken) { 1454 | super(age, furColor); 1455 | this.languageSpoken = languageSpoken; 1456 | } 1457 | 1458 | speak() { /* ... */ } 1459 | } 1460 | ``` 1461 | **[⬆ voltar para o topo](#Índice)** 1462 | 1463 | 1464 | ### Utilize métodos em cadeia, em sequência 1465 | 1466 | Esse padrão é muito útil em Javascript e você vai vê-lo em tantas bibliotecas como jQuery e Lodash. 1467 | Ele permite que seu código seja mais expressivo, e menos verboso. Por essa razão, eu digo, use métodos em cadeia e veja como seu código vai ser limpo. 1468 | Nas suas funções de classe, simplesmente retorne `this` no final de toda função, e você poderá criar uma cadeia de métodos em sequência. 1469 | 1470 | **Ruim:** 1471 | ```javascript 1472 | class Car { 1473 | constructor() { 1474 | this.make = 'Honda'; 1475 | this.model = 'Accord'; 1476 | this.color = 'white'; 1477 | } 1478 | 1479 | setMake(make) { 1480 | this.make = make; 1481 | } 1482 | 1483 | setModel(model) { 1484 | this.model = model; 1485 | } 1486 | 1487 | setColor(color) { 1488 | this.color = color; 1489 | } 1490 | 1491 | save() { 1492 | console.log(this.make, this.model, this.color); 1493 | } 1494 | } 1495 | 1496 | const car = new Car(); 1497 | car.setColor('pink'); 1498 | car.setMake('Ford'); 1499 | car.setModel('F-150'); 1500 | car.save(); 1501 | ``` 1502 | 1503 | **Bom:** 1504 | ```javascript 1505 | class Car { 1506 | constructor() { 1507 | this.make = 'Honda'; 1508 | this.model = 'Accord'; 1509 | this.color = 'white'; 1510 | } 1511 | 1512 | setMake(make) { 1513 | this.make = make; 1514 | // NOTE: Returning this for chaining 1515 | return this; 1516 | } 1517 | 1518 | setModel(model) { 1519 | this.model = model; 1520 | // NOTE: Returning this for chaining 1521 | return this; 1522 | } 1523 | 1524 | setColor(color) { 1525 | this.color = color; 1526 | // NOTE: Returning this for chaining 1527 | return this; 1528 | } 1529 | 1530 | save() { 1531 | console.log(this.make, this.model, this.color); 1532 | // NOTE: Returning this for chaining 1533 | return this; 1534 | } 1535 | } 1536 | 1537 | const car = new Car() 1538 | .setColor('pink') 1539 | .setMake('Ford') 1540 | .setModel('F-150') 1541 | .save(); 1542 | ``` 1543 | **[⬆ voltar para o topo](#Índice)** 1544 | 1545 | ### Prefira composição ao invés de herança 1546 | 1547 | Como foi dito no livro [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) pela "Gangue dos 4", 1548 | você deve preferir composições ao invés de herança sempre que puder. Existem bons motivos para usar herança e muitos para usar composição. 1549 | 1550 | O ponto principal é que se seu gosto é naturalmente utilizar herança, tente pensar se composição irá resolver seu problema de forma melhor. 1551 | Em alguns casos ela pode. 1552 | 1553 | Você pode se questionar, "Quando devo usar herança?". Depende do problema que você tem em mãos, mas a lista abaixo mostra casos onde implementar herança é vantajoso: 1554 | 1555 | 1. Sua herança representa a relação 'é-um' e não 'tem-um' (Humano->Animal vs. Usuario->detalhesUsuario) 1556 | 2. Você pode reutilizar código das classes bases (Humanos podem se mover como todos os animais) 1557 | 3. Você quer fazer mudanças globais para classes derivadas mudando a classes base. (Mudar o gasto calórico de todos os animais quando se movem). 1558 | 1559 | **Ruim:** 1560 | ```javascript 1561 | class Employee { 1562 | constructor(name, email) { 1563 | this.name = name; 1564 | this.email = email; 1565 | } 1566 | 1567 | // ... 1568 | } 1569 | 1570 | // Ruim porque Employees "tem" o dado 'tax'. EmployeeTaxData não é um tipo de Employee 1571 | class EmployeeTaxData extends Employee { 1572 | constructor(ssn, salary) { 1573 | super(); 1574 | this.ssn = ssn; 1575 | this.salary = salary; 1576 | } 1577 | 1578 | // ... 1579 | } 1580 | ``` 1581 | 1582 | **Bom:** 1583 | ```javascript 1584 | class EmployeeTaxData { 1585 | constructor(ssn, salary) { 1586 | this.ssn = ssn; 1587 | this.salary = salary; 1588 | } 1589 | 1590 | // ... 1591 | } 1592 | 1593 | class Employee { 1594 | constructor(name, email) { 1595 | this.name = name; 1596 | this.email = email; 1597 | } 1598 | 1599 | setTaxData(ssn, salary) { 1600 | this.taxData = new EmployeeTaxData(ssn, salary); 1601 | } 1602 | // ... 1603 | } 1604 | ``` 1605 | **[⬆ voltar para o topo](#Índice)** 1606 | 1607 | ##**Testes** 1608 | Testar é mais importante que entregar. Se você não tem testes ou uma quantidade inadequada, então toda vez que você faz uma entrega de código você não vai ter certeza se quebrou algo. 1609 | Decidir o quanto se deve testar é medido pelo time, mas ter 100% de cobertura (todas funções e todos os caminhos das condicionais) é como você e seu time irão atingir confiança e paz de espírito. 1610 | Isso significa que além de ter um bom framework de testes, você também precisa usar uma [boa ferramenta de cobertura](http://gotwarlost.github.io/istanbul/) 1611 | 1612 | Não tem desculpa para não escrever testes. Tem um [monte de bons frameworks de teste](http://jstherightway.org/#testing-tools), 1613 | então ache um que seu time prefere. Quando você achar um que funcione para seu time, então prefira sempre escrever testes para cada feature/módulo que surja. 1614 | Se seu método de preferência é TDD (Test Driven Development), ótimo, mas o ponto principal é ter certeza que se está atingindo as metas de cobertura definidas antes de se lançar uma feature nova, ou refatorando uma que já existe. 1615 | 1616 | ### Conceito único por teste 1617 | 1618 | **Ruim:** 1619 | ```javascript 1620 | const assert = require('assert'); 1621 | 1622 | describe('MakeMomentJSGreatAgain', () => { 1623 | it('handles date boundaries', () => { 1624 | let date; 1625 | 1626 | date = new MakeMomentJSGreatAgain('1/1/2015'); 1627 | date.addDays(30); 1628 | assert.equal('1/31/2015', date); 1629 | 1630 | date = new MakeMomentJSGreatAgain('2/1/2016'); 1631 | date.addDays(28); 1632 | assert.equal('02/29/2016', date); 1633 | 1634 | date = new MakeMomentJSGreatAgain('2/1/2015'); 1635 | date.addDays(28); 1636 | assert.equal('03/01/2015', date); 1637 | }); 1638 | }); 1639 | ``` 1640 | 1641 | **Bom:** 1642 | ```javascript 1643 | const assert = require('assert'); 1644 | 1645 | describe('MakeMomentJSGreatAgain', () => { 1646 | it('handles 30-day months', () => { 1647 | const date = new MakeMomentJSGreatAgain('1/1/2015'); 1648 | date.addDays(30); 1649 | assert.equal('1/31/2015', date); 1650 | }); 1651 | 1652 | it('handles leap year', () => { 1653 | const date = new MakeMomentJSGreatAgain('2/1/2016'); 1654 | date.addDays(28); 1655 | assert.equal('02/29/2016', date); 1656 | }); 1657 | 1658 | it('handles non-leap year', () => { 1659 | const date = new MakeMomentJSGreatAgain('2/1/2015'); 1660 | date.addDays(28); 1661 | assert.equal('03/01/2015', date); 1662 | }); 1663 | }); 1664 | ``` 1665 | **[⬆ voltar para o topo](#Índice)** 1666 | 1667 | ##**Concorrência e assincronia** 1668 | ### Utilize Promises, evite callbacks 1669 | 1670 | Callbacks não são limpos, e eles causam um aninhamento excessivo. 1671 | Com ES2015/ES6, Promises são objetos globais nativos. Use-os! 1672 | 1673 | **Ruim:** 1674 | ```javascript 1675 | require('request').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', (requestErr, response) => { 1676 | if (requestErr) { 1677 | console.error(requestErr); 1678 | } else { 1679 | require('fs').writeFile('article.html', response.body, (writeErr) => { 1680 | if (writeErr) { 1681 | console.error(writeErr); 1682 | } else { 1683 | console.log('File written'); 1684 | } 1685 | }); 1686 | } 1687 | }); 1688 | 1689 | ``` 1690 | 1691 | **Bom:** 1692 | ```javascript 1693 | require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin') 1694 | .then((response) => { 1695 | return require('fs-promise').writeFile('article.html', response); 1696 | }) 1697 | .then(() => { 1698 | console.log('File written'); 1699 | }) 1700 | .catch((err) => { 1701 | console.error(err); 1702 | }); 1703 | 1704 | ``` 1705 | **[⬆ voltar para o topo](#Índice)** 1706 | 1707 | ### Async/Await são ainda mais limpos que Promises 1708 | Promises são uma alternativa bem limpa se comparadas a callbacks, mas ES2017/ES8 traz async e await 1709 | que oferecem uma solução ainda mais limpa. Tudo que você precisa é uma função prefixada com o termo 'async', e então você 1710 | pode escrever sua lógica de maneira ordenada e imperativa sem um encadeamento de `then`. 1711 | Use isso caso você possa usar as vantangens das feature do ES2017/ES8, compilando o código babeljs por exemplo. 1712 | 1713 | **Ruim:** 1714 | ```javascript 1715 | require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin') 1716 | .then((response) => { 1717 | return require('fs-promise').writeFile('article.html', response); 1718 | }) 1719 | .then(() => { 1720 | console.log('File written'); 1721 | }) 1722 | .catch((err) => { 1723 | console.error(err); 1724 | }); 1725 | 1726 | ``` 1727 | 1728 | **Bom:** 1729 | ```javascript 1730 | async function getCleanCodeArticle() { 1731 | try { 1732 | const response = await require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin'); 1733 | await require('fs-promise').writeFile('article.html', response); 1734 | console.log('File written'); 1735 | } catch(err) { 1736 | console.error(err); 1737 | } 1738 | } 1739 | ``` 1740 | **[⬆ voltar para o topo](#Índice)** 1741 | 1742 | ##**Tratamento de erros** 1743 | 1744 | Exibir erros com throw é uma coisa boa! Isso sinaliza que sua aplicação tem um erro e faz você saber disso parando a execução do script, matando o processo (em Node), 1745 | e notificando no seu console. 1746 | 1747 | ### Não ignore erros que caem no block de 'catch' 1748 | 1749 | Não fazendo nada com o erro que cai no catch não te traz a habilidade de consertar esse error ou reagir mediante um erro. 1750 | Logar esse erro no console (`console.log`) não é muito melhor, porque muitas vezes esses logs somem no mar de coisas printadas no console. 1751 | Se você vai encapsular um código num bloco try/catch isso significa que um erro pode vir a ocorrer e pra isso você deve ter um plano, ou criar um caminho no código para quando isso ocorrer. 1752 | 1753 | **Ruim:** 1754 | ```javascript 1755 | try { 1756 | functionThatMightThrow(); 1757 | } catch (error) { 1758 | console.log(error); 1759 | } 1760 | ``` 1761 | 1762 | **Bom:** 1763 | ```javascript 1764 | try { 1765 | functionThatMightThrow(); 1766 | } catch (error) { 1767 | // One option (more noisy than console.log): 1768 | console.error(error); 1769 | // Another option: 1770 | notifyUserOfError(error); 1771 | // Another option: 1772 | reportErrorToService(error); 1773 | // OR do all three! 1774 | } 1775 | ``` 1776 | 1777 | ### Não ignore Promises rejeitadas 1778 | Pela mesma razão que você não deve ignorar erros de blocos `try/catch`. 1779 | 1780 | **Ruim:** 1781 | ```javascript 1782 | getdata() 1783 | .then((data) => { 1784 | functionThatMightThrow(data); 1785 | }) 1786 | .catch((error) => { 1787 | console.log(error); 1788 | }); 1789 | ``` 1790 | 1791 | **Bom:** 1792 | ```javascript 1793 | getdata() 1794 | .then((data) => { 1795 | functionThatMightThrow(data); 1796 | }) 1797 | .catch((error) => { 1798 | // One option (more noisy than console.log): 1799 | console.error(error); 1800 | // Another option: 1801 | notifyUserOfError(error); 1802 | // Another option: 1803 | reportErrorToService(error); 1804 | // OR do all three! 1805 | }); 1806 | ``` 1807 | 1808 | **[⬆ voltar para o topo](#Índice)** 1809 | 1810 | ##**Formatação** 1811 | 1812 | Formatação é subjetivo. Assim como muitas regras expostas aqui, não existe uma regra rápida e sólida para ser seguida. 1813 | O ponto principal é NÃO ARGUMENTE em cima de regras de formatação. 1814 | 1815 | Existem muitas [ferramentas que automatizam esse processo](http://standardjs.com/rules.html) de checagem de formatação. 1816 | Utilize uma! É uma perda de tempo e dinheiro para engenheiros em argumentar em cima de formatação. 1817 | 1818 | Para coisas que não se enquandram embaixo do padrão de formatação automática (identação, tabs vs. espaços, aspas duplas ou simples) procure orientação de outros desenvolvedores. 1819 | 1820 | ### Utilize maiúsculas de forma consistente 1821 | 1822 | Javascript não possui tipagem, então capitalização irá dizer muito sobre o significado das suas variáveis, funções, etc. 1823 | Essas regras são subjetivas, então seu time pode escolher qualquer uma que desejam. 1824 | O ponto central é, não interessa qual convenção escolher, apenas seja consistente. 1825 | 1826 | **Ruim:** 1827 | ```javascript 1828 | const DAYS_IN_WEEK = 7; 1829 | const daysInMonth = 30; 1830 | 1831 | const songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; 1832 | const Artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; 1833 | 1834 | function eraseDatabase() {} 1835 | function restore_database() {} 1836 | 1837 | class animal {} 1838 | class Alpaca {} 1839 | ``` 1840 | 1841 | **Bom:** 1842 | ```javascript 1843 | const DAYS_IN_WEEK = 7; 1844 | const DAYS_IN_MONTH = 30; 1845 | 1846 | const songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; 1847 | const artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; 1848 | 1849 | function eraseDatabase() {} 1850 | function restoreDatabase() {} 1851 | 1852 | class Animal {} 1853 | class Alpaca {} 1854 | ``` 1855 | **[⬆ voltar para o topo](#Índice)** 1856 | 1857 | ### Funções que chamam ou que são chamadas estar próximas 1858 | 1859 | Se uma função chama uma outra, mantenha essas funções perto uma das outras no código. 1860 | Idealmente mantenha a função que chama em cima da função que é chamada. Nós tendemos a ler código de cima-pra-baixo, como um jornal. 1861 | Por causa disso, faça seu código ser lido desse jeito. 1862 | 1863 | **Ruim:** 1864 | ```javascript 1865 | class PerformanceReview { 1866 | constructor(employee) { 1867 | this.employee = employee; 1868 | } 1869 | 1870 | lookupPeers() { 1871 | return db.lookup(this.employee, 'peers'); 1872 | } 1873 | 1874 | lookupManager() { 1875 | return db.lookup(this.employee, 'manager'); 1876 | } 1877 | 1878 | getPeerReviews() { 1879 | const peers = this.lookupPeers(); 1880 | // ... 1881 | } 1882 | 1883 | perfReview() { 1884 | this.getPeerReviews(); 1885 | this.getManagerReview(); 1886 | this.getSelfReview(); 1887 | } 1888 | 1889 | getManagerReview() { 1890 | const manager = this.lookupManager(); 1891 | } 1892 | 1893 | getSelfReview() { 1894 | // ... 1895 | } 1896 | } 1897 | 1898 | const review = new PerformanceReview(user); 1899 | review.perfReview(); 1900 | ``` 1901 | 1902 | **Bom:** 1903 | ```javascript 1904 | class PerformanceReview { 1905 | constructor(employee) { 1906 | this.employee = employee; 1907 | } 1908 | 1909 | perfReview() { 1910 | this.getPeerReviews(); 1911 | this.getManagerReview(); 1912 | this.getSelfReview(); 1913 | } 1914 | 1915 | getPeerReviews() { 1916 | const peers = this.lookupPeers(); 1917 | // ... 1918 | } 1919 | 1920 | lookupPeers() { 1921 | return db.lookup(this.employee, 'peers'); 1922 | } 1923 | 1924 | getManagerReview() { 1925 | const manager = this.lookupManager(); 1926 | } 1927 | 1928 | lookupManager() { 1929 | return db.lookup(this.employee, 'manager'); 1930 | } 1931 | 1932 | getSelfReview() { 1933 | // ... 1934 | } 1935 | } 1936 | 1937 | const review = new PerformanceReview(employee); 1938 | review.perfReview(); 1939 | ``` 1940 | 1941 | **[⬆ voltar para o topo](#Índice)** 1942 | 1943 | 1944 | ##**Comentários** 1945 | 1946 | Somente comente coisas que necessitam explicação de regras de negócio complexas. 1947 | Comentários são desculpas, não algo obrigatório. Bom código na maioria das vezes se auto-documenta. 1948 | 1949 | **Ruim:** 1950 | ```javascript 1951 | function hashIt(data) { 1952 | // The hash 1953 | let hash = 0; 1954 | 1955 | // Length of string 1956 | const length = data.length; 1957 | 1958 | // Loop through every character in data 1959 | for (let i = 0; i < length; i++) { 1960 | // Get character code. 1961 | const char = data.charCodeAt(i); 1962 | // Make the hash 1963 | hash = ((hash << 5) - hash) + char; 1964 | // Convert to 32-bit integer 1965 | hash &= hash; 1966 | } 1967 | } 1968 | ``` 1969 | 1970 | **Bom:** 1971 | ```javascript 1972 | 1973 | function hashIt(data) { 1974 | let hash = 0; 1975 | const length = data.length; 1976 | 1977 | for (let i = 0; i < length; i++) { 1978 | const char = data.charCodeAt(i); 1979 | hash = ((hash << 5) - hash) + char; 1980 | 1981 | // Convert to 32-bit integer 1982 | hash &= hash; 1983 | } 1984 | } 1985 | 1986 | ``` 1987 | **[⬆ voltar para o topo](#Índice)** 1988 | 1989 | ### Não deixe código comentado na sua base de código 1990 | Controle de versão existe para uma razão. Deixe código antigo na história do controle de versão. 1991 | 1992 | **Ruim:** 1993 | ```javascript 1994 | doStuff(); 1995 | // doOtherStuff(); 1996 | // doSomeMoreStuff(); 1997 | // doSoMuchStuff(); 1998 | ``` 1999 | 2000 | **Bom:** 2001 | ```javascript 2002 | doStuff(); 2003 | ``` 2004 | **[⬆ voltar para o topo](#Índice)** 2005 | 2006 | ### Não utilize comentários que parecem changelogs 2007 | Lembre-se, use controle de versão! Não existe a necessidade de código morto, código comentado, ou comentários do que foi mudado. 2008 | Utilize `git log` para visualizar a história do código. 2009 | 2010 | **Ruim:** 2011 | ```javascript 2012 | /** 2013 | * 2016-12-20: Removed monads, didn't understand them (RM) 2014 | * 2016-10-01: Improved using special monads (JP) 2015 | * 2016-02-03: Removed type-checking (LI) 2016 | * 2015-03-14: Added combine with type-checking (JR) 2017 | */ 2018 | function combine(a, b) { 2019 | return a + b; 2020 | } 2021 | ``` 2022 | 2023 | **Bom:** 2024 | ```javascript 2025 | function combine(a, b) { 2026 | return a + b; 2027 | } 2028 | ``` 2029 | **[⬆ voltar para o topo](#Índice)** 2030 | 2031 | ### Evite blocos de comentários que indicam seções no seu código 2032 | 2033 | Esses blocos geralmente adicionam ruído no seu código. 2034 | Deixe os nomes das funções e das variáveis estuturarem o código, assim como identação e formatação corretas. 2035 | 2036 | **Ruim:** 2037 | ```javascript 2038 | //////////////////////////////////////////////////////////////////////////////// 2039 | // Scope Model Instantiation 2040 | //////////////////////////////////////////////////////////////////////////////// 2041 | $scope.model = { 2042 | menu: 'foo', 2043 | nav: 'bar' 2044 | }; 2045 | 2046 | //////////////////////////////////////////////////////////////////////////////// 2047 | // Action setup 2048 | //////////////////////////////////////////////////////////////////////////////// 2049 | const actions = function() { 2050 | // ... 2051 | }; 2052 | ``` 2053 | 2054 | **Bom:** 2055 | ```javascript 2056 | $scope.model = { 2057 | menu: 'foo', 2058 | nav: 'bar' 2059 | }; 2060 | 2061 | const actions = function() { 2062 | // ... 2063 | }; 2064 | ``` 2065 | 2066 | **[⬆ voltar para o topo](#Índice)** 2067 | -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielgodoy-zz/codigo-limpo-javascript/89fff57b43f88379b75b8cc0ce11cba3d1b64819/favicon.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codigo-limpo-js", 3 | "version": "1.0.0", 4 | "description": "Repositório original: [ryanmcdermott/clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript)", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "browser-sync start -s -f *.html assets --no-ui --no-notify", 8 | "deploy": "surge --domain codigolimpojs.surge.sh .", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+ssh://git@gitlab.com/gabrielgodoy/codigo-limpo-js.git" 14 | }, 15 | "keywords": [], 16 | "author": "", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://gitlab.com/gabrielgodoy/codigo-limpo-js/issues" 20 | }, 21 | "homepage": "https://gitlab.com/gabrielgodoy/codigo-limpo-js#README" 22 | } 23 | --------------------------------------------------------------------------------