Kaique Mitsuo Silva Yamamoto
Arquitetura softwareFrontend webTypeScriptAnti-patterns & Code Smells

Anti-patterns & Code Smells

Padrões que parecem bons mas causam problemas — God Object, Spaghetti Code, Copy-Paste, Over-engineering e como identificá-los.

Anti-patterns & Code Smells

Anti-patterns são soluções tentadoras que parecem corretas mas causam problemas a longo prazo. Code smells são sinais de que algo no código não está certo — como um cheiro estranho na cozinha que indica que algo apodreceu.

Mapa de estudo

Os 7 anti-patterns mais comuns

1. God Object / God Function

Uma classe ou função que faz tudo — cresce até se tornar impossível de entender ou manter.

// ❌ God Function — 200 linhas, 15 responsabilidades
async function processOrder(req: Request, res: Response) {
  // 1. Valida body
  // 2. Busca usuário
  // 3. Verifica estoque
  // 4. Calcula frete
  // 5. Aplica cupom
  // 6. Calcula impostos
  // 7. Cobre pagamento
  // 8. Cria pedido
  // 9. Atualiza estoque
  // 10. Envia email
  // 11. Envia WhatsApp
  // 12. Atualiza analytics
  // 13. Loga auditoria
  // 14. Invalida cache
  // 15. Retorna response
}

// ✅ Separado — cada coisa no seu lugar

Sintoma: Função com mais de 50 linhas, mais de 5 parâmetros, ou mais de 3 níveis de aninhamento.

2. Spaghetti Code

Código com fluxo de controle desorganizado — goto disfarçado de callbacks, promises aninhadas, lógica saltando entre arquivos sem padrão.

// ❌ Spaghetti — nested callbacks, fluxo impossível de rastrear
getUser(id, (user) => {
  getOrders(user.id, (orders) => {
    orders.forEach((order) => {
      getItems(order.id, (items) => {
        items.forEach((item) => {
          getStock(item.productId, (stock) => {
            if (stock > 0) {
              // ... mais 5 níveis
            }
          });
        });
      });
    });
  });
});

// ✅ Flat — async/await, fluxo linear
const user = await getUser(id);
const orders = await getOrders(user.id);
for (const order of orders) {
  const items = await getItems(order.id);
  // fluxo claro, linha a linha
}

3. Copy-Paste Programming

Código duplicado que "funciona porque copiei de onde funcionava". Quando uma regra muda, você precisa achar e alterar todos os copies.

Sintoma: Mesmo bloco de código aparecendo em 3+ lugares.

Solução: DRY — extraia uma função, componente ou utilitário.

4. Over-engineering

Solução complexa para um problema simples. Factory de factories, abstraction layers, design patterns onde uma função resolveria.

// ❌ Over-engineering — para formatar uma data
class DateFormatterFactory {
  static create(locale: string, timezone: string, options: FormatOptions): DateFormatter {
    const strategy = DateFormatStrategyFactory.create(locale);
    const adapter = new TimezoneAdapter(timezone);
    return new DateFormatter(strategy, adapter, options);
  }
}

// ✅ Simples
function formatDate(date: Date): string {
  return date.toLocaleDateString('pt-BR');
}

5. Primitive Obsession

Usar tipos primitivos (string, number, boolean) para representar conceitos complexos que deveriam ter seu próprio tipo.

// ❌ Primitive obsession — o que é essa string?
function createUser(name: string, email: string, role: string, status: string) {
  // role pode ser qualquer string — 'admin', 'ADMIN', 'adm', 'banana'
  // status pode ser qualquer string — 'active', 'ACTIVE', 'ativo', '1'
}

// ✅ Tipos específicos — o compilador garante validade
type Role = 'admin' | 'editor' | 'viewer';
type Status = 'active' | 'inactive' | 'pending';

interface CreateUserDto {
  name: string;
  email: Email;       // tipo refinado
  role: Role;
  status: Status;
}

6. Boolean Trap

Funções com múltiplos booleanos onde fica impossível saber o que cada true/false significa.

// ❌ Boolean trap — o que true, false, true significam?
createUser('Kaique', '[email protected]', true, false, true);

// ✅ Objeto nomeado
createUser({
  name: 'Kaique',
  email: '[email protected]',
  isAdmin: true,
  sendWelcomeEmail: false,
  isActive: true,
});

7. Arrow Function Anti-pattern

// ❌ Arrow function retornando void por engano
items.forEach(item => {
  processItem(item); // se processItem retorna Promise, essa promise é IGNORADA
});

// ✅ Para promises, use for...of
for (const item of items) {
  await processItem(item);
}

// ✅ Para map, use o retorno
const results = items.map(item => processItem(item)); // retorna array de resultados

Referências

On this page