Recap do material — code smells, complexidade ciclomática, cobertura, duplicação e RNFs mensuráveis.
Refatoração guiada por métricas: medir com ESLint, jscpd e Jest, refatorar e provar a melhora — tudo local.
Pausa para refeição.
Medir o projeto, eleger os 2 piores pontos, refatorar e registrar as métricas antes/depois no MR.
Sair com o projeto medido e melhorado: complexidade e duplicação reduzidas nos piores pontos, cobertura com threshold local e um baseline de desempenho — com a suite verde provando que nada quebrou.
Jest nos services e helpers, supertest nos controllers, AAA, mocks do repository. A suite existe e roda — isso não é o assunto de hoje.
O código funciona — mas ele é bom? Funcionar é requisito funcional. Ser legível, simples, rápido e sustentável é requisito não funcional.
Complexidade ciclomática, % de duplicação, cobertura com threshold, latência p99. Cada um vira um número — e número dá para comparar antes e depois do refactor.
eslint, jest --coverage, jscpd, madge, autocannon. Um npx e pronto — nada de servidor, nada de CI. A máquina de vocês é o laboratório.
total * 0.9 — 0.9 é o quê? Desconto? Taxa? Ninguém sabe.getUser() que também cria, atualiza e envia e-mail.Refatorar = mudar a estrutura sem mudar o comportamento. Quem prova que o comportamento não mudou é a suite de testes: verde antes, refactor pequeno, verde depois. Sem suite verde, não é refactor — é aposta.
Quantos trajetos diferentes existem da sua casa até o Inteli? Sem cruzamentos, um só. Cada cruzamento com decisão cria um trajeto novo. Para conhecer a cidade inteira, você precisa percorrer cada trajeto pelo menos uma vez — no código, isso é um teste por caminho.
Complexidade ciclomática é o número de trajetos independentes da entrada à saída da função. Cada if que você escreve é uma promessa silenciosa: "alguém vai precisar de mais um teste".
Começa em 1 e soma 1 para cada if, else if, for, while, case, catch, &&, || e ?:. O resultado é o número mínimo de testes para cobrir todos os galhos.
Complexidade alta = mais caminhos = mais testes necessários = mais lugares para o bug se esconder. É o RNF de manutenibilidade em forma de número.
complexity transforma a métrica em aviso automáticocomplexity — ciclomática: quantos caminhos a máquina percorresonarjs/cognitive-complexity — cognitiva: quão difícil é para um humano ler (aninhamento pesa mais)Roda no terminal com npx eslint src/ e no editor enquanto você digita. O limite (8, 10) é da equipe — o que passa do limite entra na lista de refactor do Bloco 4.
TAXAS[tipo]npm test verde → refactor pequeno → npm test verde → npx eslint para ver a métrica cair → commit. Passos curtos: se quebrar, o suspeito é o último passo.
Na pressa, o ciclo vira Red → Green → próxima feature. O débito acumula até o código travar. Hoje o REFACTOR deixa de ser opcional: ele tem métrica, tem meta e tem entrega.
Os testes escritos desde a aula 6 são a rede de segurança. Cada refactor de hoje roda contra eles: comportamento igual, estrutura melhor, número menor.
Só este teste rodou: desconto({'{'}vip: false{'}'}, 100)
function desconto(user, total) { if (user.vip) return total * 0.8; ← nunca executou return total; }
Se o galho user.vip = true nunca foi testado, a lógica de desconto VIP pode estar errada — e o relatório ainda mostra verde em Lines e Functions.
| Métrica | O que conta | No exemplo | Peso |
|---|---|---|---|
| Lines | Linhas físicas tocadas ao menos 1x | 3/3 = 100% | ⭐ |
| Stmts | Instruções executadas (1 linha pode ter várias) | 2/3 = 67% | ⭐⭐ |
| Functions | Funções chamadas ao menos 1x | 1/1 = 100% | ⭐⭐ |
| Branches | Cada galho de if/else, ternário, ??, && |
1/2 = 50% | ⭐⭐⭐ |
Branches é a mais honesta: obriga você a cobrir cada decisão, não só passar pela linha. Configure no jest.config: branches ≥ 70% e lines ≥ 80% — o threshold que falha antes de virar dívida técnica.
if/else testados"coverageThreshold": {
"global": { "branches": 70, "lines": 80 }
}
Abaixo disso, npm run test:cov falha na sua máquina — o portão de qualidade é local.
Linha tocada sem assert não testa nada. Use coverage para achar buracos, nunca como troféu.
npx que revelam o que o olho não vêAcha trechos duplicados por token, não por texto — pega até cópia com variável renomeada. Duplicação é o smell mais caro: bug corrigido em um lugar continua vivo no outro.
--circular denuncia ciclos (A importa B que importa A): impossíveis de testar isolados e de entender separados. Service e repository em ciclo = camadas vazando.
Duplicação < 5% · ciclos = 0. Rodem nos dois sentidos: antes do refactor (diagnóstico) e depois (prova).
A média esconde os piores casos. p50 = experiência típica; p99 = a do usuário azarado. RNF sério se escreve em percentil: "p99 < 100 ms".
Rode antes do refactor e guarde os números. Rode depois e compare. Refatorar não pode deixar o endpoint mais lento — agora você tem como saber.
O gargalo quase sempre é a query. Volte ao EXPLAIN das aulas de banco — índice faltando aparece no p99 primeiro.
Esta aula: as 5 visões atravessadas pelo eixo de qualidade mensurável — métricas locais dão segurança para refatorar.
Refatorar o código mantendo o comportamento observável — provado pela suite de testes rodando verde antes e depois.
| MANT Manutenibilidade | ✅ Complexidade ≤ 8 · duplicação < 5% |
| CONF Confiabilidade | ✅ Suite verde pós-refactor |
| TEST Testabilidade | ✅ Cobertura com threshold local |
| DES Desempenho | ✅ p99 medido com autocannon |
| PORT Portabilidade | → Métricas reproduzíveis em qualquer máquina |
npx eslint src/
Qual arquivo tem mais warnings? Liste os 3 piores.
npx eslint src/ --rule "complexity:[warn,5]"
Quantas funções passam de 5 caminhos? Qual é a mais alta?
npm i -D eslint-plugin-sonarjs
Configure o .eslintrc com sonarjs. Quantos problemas novos?
identifique + aplique guard clauses
Pegue a função mais complexa e mostre antes/depois.
npm run test:cov
Qual % de branches abaixo de 80%? Qual controller tem menos cobertura?
npx jscpd src && npx madge --circular src
% de duplicação — há dependência circular?
npx autocannon -c 50 -d 10 localhost:3000/api/…
Req/s e p99 — passa de 100 ms?