---
id: clean-code-principles
domain: engineering
agents: [dev]
when: "ao escrever ou refatorar qualquer código, para garantir legibilidade e manutenibilidade"
---

# Clean Code — heurísticas de legibilidade e manutenção

Código se lê muito mais vezes do que se escreve. A régua aqui é a do `clean-code-javascript`
de Ryan McDermott (adaptação do _Clean Code_ de Robert C. Martin para JS) — não um style guide,
mas um guia para produzir software **legível, reutilizável e refatorável**. Os exemplos abaixo são
os pares reais do repositório-fonte (JavaScript). A regra-mãe: **toda decisão de código tem que se
justificar** — se um nome, um argumento ou uma condicional só existe porque "saiu assim", é dívida.

> Aviso do próprio autor: nem todo princípio precisa ser seguido à risca, e poucos são unânimes. São
> diretrizes. Mas elas vêm de anos de experiência coletiva — use-as como pedra-de-toque ao avaliar a
> qualidade do código que você produz.

## O problema / os tells

Se o diff tem 3+ destes, ele cheira a código apressado, não a craft:

1. **Nomes não-pesquisáveis e mágicos** — `setTimeout(blastOff, 86400000)`. Ninguém sabe o que é
   `86400000`, e você não consegue dar `grep` num número solto no meio do código.
2. **Nomes não-pronunciáveis / abreviados** — `yyyymmdstr`, `l`, `d1`. Se você não consegue falar o
   nome em voz alta numa code review, ele está errado.
3. **Vocabulário inconsistente pro mesmo conceito** — `getUserInfo()`, `getClientData()`,
   `getCustomerRecord()` espalhados, todos buscando a mesma coisa.
4. **Funções com 4+ argumentos posicionais** — `createMenu("Foo", "Bar", "Baz", true)`. Na chamada,
   ninguém sabe o que cada posição significa, e o `true` no fim é um enigma.
5. **Argumento-flag booleano** — `createFile(name, temp)`. A flag denuncia que a função tem dois
   caminhos, logo faz mais de uma coisa.
6. **Função que faz várias coisas** — filtra, transforma e dispara no mesmo corpo; impossível de
   testar ou reusar em pedaços.
7. **Side effects escondidos** — função que muta uma variável global ou o objeto recebido por
   parâmetro, em vez de receber e retornar.
8. **Condicionais negativas e cruas** — `if (!isDOMNodeNotPresent(node))`: dupla negação que trava o
   leitor; `if (fsm.state === "fetching" && isEmpty(listNode))` sem nome que explique a intenção.
9. **Herança por preguiça** — `EmployeeTaxData extends Employee` quando a relação é "tem-um", não "é-um".
10. **`catch (error) { console.log(error) }`** — engole o erro sem plano de reação.
11. **Código morto** — `oldRequestModule` que ninguém chama, mantido "por via das dúvidas". O histórico
    do git já guarda isso.

## Os princípios (com pares bad→good reais)

### 1. Nomes pesquisáveis (sem números mágicos)

Constantes nomeadas em vez de literais soltos — para o leitor entender e para o `grep` achar.

```javascript
// Bad — o que diabos é 86400000?
setTimeout(blastOff, 86400000);

// Good — constante nomeada e maiúscula
const MILLISECONDS_PER_DAY = 60 * 60 * 24 * 1000; // 86400000

setTimeout(blastOff, MILLISECONDS_PER_DAY);
```

### 2. Nomes pronunciáveis e significativos

Você lê mais código do que escreve; abreviação economiza segundos na escrita e custa minutos na leitura.

```javascript
// Bad
const yyyymmdstr = moment().format("YYYY/MM/DD");

// Good
const currentDate = moment().format("YYYY/MM/DD");
```

E sem mapeamento mental: nomeie a variável do loop pelo que ela é, não `l`.

```javascript
// Bad — "espera, o que é `l` mesmo?"
locations.forEach(l => { /* ...muitas linhas... */ dispatch(l); });

// Good
locations.forEach(location => { /* ... */ dispatch(location); });
```

### 3. Mesmo vocabulário pro mesmo tipo

Um conceito, um nome. Escolha um e use em todo lugar.

```javascript
// Bad
getUserInfo();
getClientData();
getCustomerRecord();

// Good
getUser();
```

### 4. ≤2 argumentos — objeto quando precisar de mais

Mais de 2-3 parâmetros leva à explosão combinatória de casos de teste e some com a clareza na chamada.
Quando precisar de muitos, passe **um objeto** e desestruture — a assinatura passa a documentar quais
campos a função usa, e simula parâmetros nomeados.

```javascript
// Bad — o que é `true`? e a ordem?
function createMenu(title, body, buttonText, cancellable) { /* ... */ }
createMenu("Foo", "Bar", "Baz", true);

// Good — campos nomeados, ordem irrelevante
function createMenu({ title, body, buttonText, cancellable }) { /* ... */ }
createMenu({ title: "Foo", body: "Bar", buttonText: "Baz", cancellable: true });
```

### 5. Função faz UMA coisa

A regra mais importante. Quando a função faz mais de uma coisa, é difícil compor, testar e raciocinar.
Isole cada ação.

```javascript
// Bad — itera, busca no banco, checa estado e envia, tudo junto
function emailClients(clients) {
  clients.forEach(client => {
    const clientRecord = database.lookup(client);
    if (clientRecord.isActive()) {
      email(client);
    }
  });
}

// Good — cada função tem um trabalho
function emailActiveClients(clients) {
  clients.filter(isActiveClient).forEach(email);
}

function isActiveClient(client) {
  const clientRecord = database.lookup(client);
  return clientRecord.isActive();
}
```

### 6. Sem flag booleana como argumento

A flag avisa que a função tem caminhos diferentes — ou seja, faz mais de uma coisa. Separe.

```javascript
// Bad
function createFile(name, temp) {
  if (temp) {
    fs.create(`./temp/${name}`);
  } else {
    fs.create(name);
  }
}

// Good
function createFile(name) {
  fs.create(name);
}

function createTempFile(name) {
  createFile(`./temp/${name}`);
}
```

### 7. Evite side effects

Uma função idealmente recebe valores e retorna valores — não muta estado global nem o objeto/array
recebido. Mutar dado compartilhado gera bugs em lugares distantes. Centralize os efeitos que forem
inevitáveis (ex.: escrever em arquivo) em um único serviço.

```javascript
// Bad — muta uma global; outra função que use `name` quebra
let name = "Ryan McDermott";
function splitIntoFirstAndLastName() {
  name = name.split(" ");
}

// Good — recebe e retorna, sem tocar no de fora
function splitIntoFirstAndLastName(name) {
  return name.split(" ");
}
const newName = splitIntoFirstAndLastName(name);
```

O mesmo vale para arrays/objetos passados por parâmetro: clone em vez de mutar.

```javascript
// Bad — muta o cart de quem chamou
const addItemToCart = (cart, item) => {
  cart.push({ item, date: Date.now() });
};

// Good — retorna um novo cart
const addItemToCart = (cart, item) => {
  return [...cart, { item, date: Date.now() }];
};
```

### 8. Evite condicionais negativas

Negativa (ainda mais dupla) força o leitor a inverter a lógica na cabeça. Nomeie no positivo.

```javascript
// Bad — dupla negação
function isDOMNodeNotPresent(node) { /* ... */ }
if (!isDOMNodeNotPresent(node)) { /* ... */ }

// Good
function isDOMNodePresent(node) { /* ... */ }
if (isDOMNodePresent(node)) { /* ... */ }
```

### 9. Encapsule condicionais

Uma expressão booleana composta vira uma função com nome que diz a intenção.

```javascript
// Bad
if (fsm.state === "fetching" && isEmpty(listNode)) { /* ... */ }

// Good
function shouldShowSpinner(fsm, listNode) {
  return fsm.state === "fetching" && isEmpty(listNode);
}
if (shouldShowSpinner(fsmInstance, listNodeInstance)) { /* ... */ }
```

### 10. Composição > herança

Se sua cabeça já foi pra `extends`, pare e pergunte se é "é-um" (herança) ou "tem-um" (composição).
`EmployeeTaxData` não é um tipo de `Employee` — um empregado **tem** dados fiscais.

```javascript
// Bad — herança forçada numa relação "tem-um"
class EmployeeTaxData extends Employee {
  constructor(ssn, salary) {
    super();
    this.ssn = ssn;
    this.salary = salary;
  }
}

// Good — composição
class Employee {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
  setTaxData(ssn, salary) {
    this.taxData = new EmployeeTaxData(ssn, salary);
  }
}
```

### 11. SOLID em JS — os cinco

**S — Single Responsibility.** Uma classe, uma razão para mudar. Separe autenticação de configuração.

```javascript
// Bad — UserSettings também verifica credenciais
class UserSettings {
  changeSettings(settings) {
    if (this.verifyCredentials()) { /* ... */ }
  }
  verifyCredentials() { /* ... */ }
}

// Good — auth virou colaboradora
class UserAuth {
  constructor(user) { this.user = user; }
  verifyCredentials() { /* ... */ }
}
class UserSettings {
  constructor(user) {
    this.user = user;
    this.auth = new UserAuth(user);
  }
  changeSettings(settings) {
    if (this.auth.verifyCredentials()) { /* ... */ }
  }
}
```

**O — Open/Closed.** Aberto para extensão, fechado para modificação. Em vez de `if adapter.name === ...`,
deixe cada adapter implementar o contrato.

```javascript
// Bad — fetch precisa mudar a cada novo adapter
fetch(url) {
  if (this.adapter.name === "ajaxAdapter") { return makeAjaxCall(url).then(/*...*/); }
  else if (this.adapter.name === "nodeAdapter") { return makeHttpCall(url).then(/*...*/); }
}

// Good — cada adapter tem request(); HttpRequester não muda
fetch(url) {
  return this.adapter.request(url).then(response => { /* transform */ });
}
```

**L — Liskov Substitution.** Subtipo tem que substituir o tipo base sem quebrar resultados. O clássico
Square/Rectangle: forçar `Square extends Rectangle` faz `getArea()` mentir. Modele ambos sobre `Shape`.

```javascript
// Good — sem herança que viola contrato
class Shape { render(area) { /* ... */ } }
class Rectangle extends Shape {
  constructor(width, height) { super(); this.width = width; this.height = height; }
  getArea() { return this.width * this.height; }
}
class Square extends Shape {
  constructor(length) { super(); this.length = length; }
  getArea() { return this.length * this.length; }
}
```

**I — Interface Segregation.** Não force o cliente a depender do que não usa. Settings opcionais em vez
de "fat interface".

```javascript
// Good — animationModule é opcional, dentro de options
const $ = new DOMTraverser({
  rootNode: document.getElementsByTagName("body"),
  options: { animationModule() {} }
});
```

**D — Dependency Inversion.** Módulo de alto nível não depende do de baixo nível — ambos dependem de
abstração. Injete a dependência em vez de instanciá-la dentro.

```javascript
// Bad — InventoryTracker cria a dependência concreta
class InventoryTracker {
  constructor(items) {
    this.items = items;
    this.requester = new InventoryRequester(); // acoplado
  }
}

// Good — requester é injetado (qualquer um com requestItem serve)
class InventoryTracker {
  constructor(items, requester) {
    this.items = items;
    this.requester = requester;
  }
}
```

### 12. Lance objetos Error, não strings; e nunca ignore o erro capturado

Erros lançados são bons: o runtime te avisa que algo deu errado, com stack trace. Lance um `Error` de
verdade (carrega mensagem **e** stack), e dê um plano para o `catch` — não engula.

```javascript
// Tell — string crua não tem stack trace
throw "Must be of type String or Number";

// Good — Error object, com stack
throw new Error("Must be of type String or Number");
```

```javascript
// Bad — engole o erro
try {
  functionThatMightThrow();
} catch (error) {
  console.log(error);
}

// Good — reaja: log de erro, notifique, ou reporte
try {
  functionThatMightThrow();
} catch (error) {
  console.error(error);
  notifyUserOfError(error);
  reportErrorToService(error);
}
```

O mesmo vale para promises rejeitadas: trate no `.catch`, não só `console.log`.

### 13. Remova código morto

Se ninguém chama, apague. O histórico do git guarda. `oldRequestModule` parado ao lado do `new` só
confunde quem lê.

```javascript
// Bad — função antiga nunca chamada, mantida "por garantia"
function oldRequestModule(url) { /* ... */ }
function newRequestModule(url) { /* ... */ }
const req = newRequestModule;

// Good — só o que está em uso
function newRequestModule(url) { /* ... */ }
const req = newRequestModule;
```

## Checklist

Antes de marcar a task como pronta, responda — qualquer "sim" é dívida a corrigir:

- [ ] Tem número/string mágico que deveria ser constante nomeada e pesquisável?
- [ ] Algum nome é abreviado, não-pronunciável ou exige mapeamento mental (`l`, `d1`, `yyyymmdstr`)?
- [ ] O mesmo conceito aparece com nomes diferentes (`getUserInfo`/`getClientData`/...)?
- [ ] Alguma função tem mais de 2-3 argumentos posicionais sem virar objeto?
- [ ] Há argumento booleano (flag) que denuncia dois caminhos na mesma função?
- [ ] Alguma função faz mais de uma coisa (mistura busca, transformação e efeito)?
- [ ] Alguma função muta estado global ou o objeto/array recebido por parâmetro?
- [ ] Há condicional negativa (ou dupla negação) que poderia ser positiva?
- [ ] Há booleano composto cru no `if` que mereceria uma função nomeada?
- [ ] Usei `extends` numa relação "tem-um" em vez de composição?
- [ ] Lancei string em vez de `new Error(...)`? Engoli algum erro com `console.log` sem reação?
- [ ] Sobrou código morto (função/branch que ninguém chama)?

## Tabela de decisão

| Use isto | Quando |
|---|---|
| Constante nomeada (`MILLISECONDS_PER_DAY`) | houver qualquer literal numérico/string com significado no código |
| Argumento objeto + desestruturação | a função precisar de mais de 2-3 parâmetros |
| Duas funções separadas | você fosse passar uma flag booleana para escolher o caminho |
| Receber + retornar (sem mutar) | a função tocaria estado global ou o objeto/array recebido |
| Clonar (`[...cart, item]`) | precisar "alterar" um array/objeto vindo por parâmetro |
| Função nomeada (`shouldShowSpinner`) | um `if` tiver booleano composto ou intenção não-óbvia |
| Condicional no positivo | o nome/teste estiver no negativo (ou em dupla negação) |
| Composição (`has-a`) | a relação for "tem-um" (`Employee` tem `TaxData`) |
| Herança (`is-a`) | for genuinamente "é-um", houver reuso da base e mudança global desejada |
| Injeção de dependência (DIP) | um módulo de alto nível instanciaria diretamente um concreto de baixo nível |
| Polimorfismo / contrato comum (OCP, LSP) | um `if/switch` ramificar por `type`/`name` e tender a crescer |
| `throw new Error(...)` + reação no `catch` | algo puder dar errado e você precisar de stack trace e plano |
| Apagar (confiar no git) | o código não for chamado por ninguém |
