Projetar uma arquitetura do zero é intelectualmente elegante; conviver com um sistema de 5–10 anos, cheio de technical debt, é a realidade da maioria das equipes.
Todo sistema de software bem-sucedido eventualmente se torna legado. À medida que funcionalidades são adicionadas, requisitos evoluem e diferentes desenvolvedores contribuem ao longo dos anos, o código acumula o que chamamos de dívida técnica. Métodos que começaram com 20 linhas crescem para 200. Classes projetadas para uma responsabilidade passam a gerenciar cinco. Padrões que faziam sentido em 2018 tornam-se anti-padrões em 2025.
Codebases legadas acumulam code smells, dependências acopladas e decisões históricas pouco documentadas, tornando a evolução cara e arriscada. A resposta clássica foi refatoração manual guiada por catálogos como o de Fowler, apoiada por ferramentas de análise estática como SonarQube. De 2020 para cá, porém, surgiu um novo ator: LLMs e ferramentas de IA que sugerem refatorações, otimizam consultas, auxiliam em paralelização e até aplicam transformações automaticamente em milhares de arquivos.
Estudos empíricos recentes indicam que LLMs são especialmente eficazes para refatorações localizadas (Rename, Extract Method) quando bem orientados por contexto e integrados a ferramentas de análise estática. Nesses cenários, há ganho de produtividade mensurável e redução de complexidade sem aumento significativo de bugs, desde que refatorações sejam validadas por testes adequados. Por outro lado, refatorações globais que impactam arquitetura continuam dependendo de julgamento humano sobre requisitos não funcionais e semântica de negócio [1, 2, 3].
Princípio Fundamental 1: Refatorar sem entender o problema é trocar technical debt explícita por dívida oculta. Testes automatizados são rede de segurança — mas não garantia absoluta de preservação de comportamento.
Princípio Fundamental 2: Refatoração segura exige testes automatizados como rede de segurança. Sem testes, qualquer refatoração — manual ou assistida por IA — é uma aposta no escuro.
Esta discussão se insere na fronteira entre design e projeto de arquitetura (Aula 5) e melhoria de sistemas existentes: como usar IA para evoluir arquiteturas reais sem transformar o sistema em laboratório de experimentos arriscados.
Code Smells: Da Detecção Manual à Automatizada
Code smells são sintomas de problemas de design que degradam manutenibilidade e aumentam technical debt, sem necessariamente indicar bugs imediatos. Catálogos contemporâneos consolidam dezenas de smells, incluindo Long Method, God Class, Duplicate Code e Feature Envy. A detecção manual, baseada em leitura de código e experiência, é cognitivamente pesada e tende a se concentrar apenas em módulos "famosamente" problemáticos.
Ferramentas de análise estática evoluíram para detectar automaticamente muitos desses smells com base em métricas como complexidade ciclomática, linhas de código, número de dependências e acoplamento. Pesquisas em machine learning para detecção de smells usam features de código para treinar classificadores com acurácia competitiva em relação a especialistas humanos — alguns estudos reportam acurácias acima de 80% para smells estruturais [4, 5]. A tabela a seguir compara as abordagens:
| Code Smell | Detecção Manual | Detecção por IA | Refatoração Sugerida | Risco |
|---|---|---|---|---|
| Long Method | Contagem visual de linhas | LOC > 50, CC > 10 | Extract Method | Baixo |
| God Class | Análise de responsabilidades | Methods > 15, Fan-out > 10 | Extract Class | Médio |
| Duplicate Code | Memória e diff visual | Similaridade AST > 85% | Extract Function | Baixo |
| Feature Envy | Análise de dependências | Coupling metrics elevadas | Move Method | Médio |
| Data Clumps | Identificação de padrões | Parâmetros frequentemente agrupados | Parameter Object | Baixo |
LLMs adicionam uma nova camada: a capacidade de interpretar código e comentários em linguagem natural, além de reconhecer padrões de design típicos. Estudos empíricos indicam que, quando bem guiados por prompts e contexto estático, modelos como GPT conseguem propor refatorações adequadas em mais de 60% dos casos, especialmente para refatorações localizadas. Experimentos com plugins como EM-Assist mostram que combinações "regra + IA generativa" se aproximam das refatorações praticadas por desenvolvedores em mais da metade dos casos [6, 7].
Adicionalmente, a vantagem da IA não está apenas na detecção, mas na escala. Um desenvolvedor experiente pode revisar algumas centenas de linhas por hora; uma ferramenta automatizada analisa centenas de milhares em minutos.
Refatoração Assistida: Long Method em Profundidade
O Long Method é possivelmente o code smell mais comum. Considere o seguinte serviço de processamento de pedidos em TypeScript, um exemplo realista de código que acumula responsabilidades ao longo do tempo:
// ====================================
// ANTES: Long Method (Code Smell)
// ====================================
class OrderService {
async processOrder(orderId: string): Promise<void> {
// Método com 100+ linhas fazendo TUDO:
// validação, cálculo, estoque, email, log
const order = await this.db.orders.findById(orderId);
if (!order) {
throw new Error('Order not found');
}
if (order.status !== 'pending') {
throw new Error(`Invalid status: ${order.status}`);
}
// Cálculo de preço - Feature Envy (acessa muito Product)
let total = 0;
for (const item of order.items) {
const product = await this.db.products.findById(item.productId);
if (!product) {
throw new Error(`Product ${item.productId} not found`);
}
if (product.stock < item.quantity) {
throw new Error(`Insufficient stock for ${product.name}`);
}
total += product.price * item.quantity;
}
// Aplicação de desconto - lógica de negócio misturada
if (order.couponCode) {
const coupon = await this.db.coupons.findByCode(order.couponCode);
if (coupon && coupon.expiresAt > new Date()) {
if (total >= coupon.minimumPurchase) {
const discount = total * (coupon.discountPercent / 100);
total -= discount;
}
}
}
// Atualização de estoque
for (const item of order.items) {
await this.db.inventory.decrease(item.productId, item.quantity);
}
// Notificação - responsabilidade completamente diferente
await this.emailService.send({
to: order.customerEmail,
subject: `Order Confirmation #${orderId}`,
body: `Dear ${order.customerName}, your order totals $${total.toFixed(2)}`
});
// Persistência final
await this.db.orders.update(orderId, {
status: 'confirmed',
total,
processedAt: new Date()
});
}
}
Este método viola o Single Responsibility Principle: valida, calcula preços, aplica descontos, atualiza estoque, envia emails e persiste dados. Testá-lo requer mockar múltiplas dependências simultaneamente. A versão refatorada separa responsabilidades.
Ferramentas modernas, combinando análise de métricas com LLM, sugerem Extract Method para isolar cálculo de total e aplicação de desconto, além de separar responsabilidades de notificação. A versão refatorada:
// ====================================
// DEPOIS: Extract Method + Modularidade
// ====================================
class PricingService {
async calculateOrderTotal(order: Order): Promise<number> {
const subtotal = await this.calculateSubtotal(order);
const discount = await this.applyDiscount(subtotal, order.couponCode);
return subtotal - discount;
}
private async calculateSubtotal(order: Order): Promise<number> {
let subtotal = 0;
for (const item of order.items) {
const product = await this.db.products.findById(item.productId);
subtotal += product.price * item.quantity;
}
return subtotal;
}
private async applyDiscount(
subtotal: number,
couponCode?: string
): Promise<number> {
if (!couponCode) return 0;
const coupon = await this.db.coupons.findByCode(couponCode);
if (!coupon || coupon.expiresAt <= new Date()) return 0;
if (subtotal < coupon.minimumPurchase) return 0;
return subtotal * (coupon.discountPercent / 100);
}
}
class OrderService {
constructor(
private pricingService: PricingService,
private inventoryService: InventoryService,
private notificationService: NotificationService
) {}
async processOrder(orderId: string): Promise<void> {
const order = await this.validateOrder(orderId);
const total = await this.pricingService.calculateOrderTotal(order);
await this.inventoryService.reserveItems(order);
await this.notificationService.sendConfirmation(order, total);
await this.finalizeOrder(orderId, total);
}
private async validateOrder(orderId: string): Promise<Order> {
const order = await this.db.orders.findById(orderId);
if (!order) throw new Error('Order not found');
if (order.status !== 'pending') {
throw new Error(`Invalid status: ${order.status}`);
}
return order;
}
private async finalizeOrder(orderId: string, total: number): Promise<void> {
await this.db.orders.update(orderId, {
status: 'confirmed',
total,
processedAt: new Date()
});
}
}
Os benefícios são claros! O PricingService.applyDiscount() pode ser testado isoladamente com mocks simples — não precisa de mock de email ou inventário. O método processOrder() lê como documentação: valida, calcula, reserva, notifica, finaliza. Estudos com LLMs mostram que, quando instruídos explicitamente sobre o tipo de refatoração desejada, a taxa de sucesso em produzir transformações sem erro de compilação e preservando testes chega a patamares acima de 80% [2, 3].
No entanto, para refatorações de maior escopo — como quebrar God Classes em múltiplos serviços — ainda há dificuldade em manter contratos, invariantes de negócio e semântica distribuída [15].
God Class: Quando Coesão se Perde
Uma God Class acumula responsabilidades que deveriam estar distribuídas. Ferramentas como SonarQube detectam esse smell via número de métodos, campos e dependências:
// ANTES: God Class típica
class UserManager {
// Autenticação
async login(email: string, password: string) { /* ... */ }
async logout(userId: string) { /* ... */ }
async resetPassword(email: string) { /* ... */ }
// Autorização
async hasPermission(userId: string, resource: string) { /* ... */ }
async assignRole(userId: string, roleId: string) { /* ... */ }
// Perfil
async updateProfile(userId: string, data: ProfileData) { /* ... */ }
async uploadAvatar(userId: string, file: File) { /* ... */ }
// Notificações
async sendNotification(userId: string, message: string) { /* ... */ }
async getNotificationPreferences(userId: string) { /* ... */ }
// Billing
async updateSubscription(userId: string, plan: Plan) { /* ... */ }
async processPayment(userId: string, amount: number) { /* ... */ }
}
Ferramentas como SonarQube detectam God Class via número de métodos, campos e responsabilidades, e LLMs podem sugerir Extract Class segmentando por bounded contexts naturais (autenticação, autorização, perfil, notificação). A refatoração resultante tende a se aproximar de uma arquitetura mais alinhada a serviços e, quando combinada com testes automatizados, reduz complexidade e melhora coesão. A questão crítica para debate é até que ponto aceitar automaticamente tais sugestões em sistemas com nuances de negócio pouco documentadas.
Otimização de Performance: Gargalos, Cache e Trade-offs
Além de refatoração estrutural, IA vem sendo aplicada à otimização de performance. Ferramentas de profiling com suporte a IA analisam métricas de latência, throughput e consumo de recursos, identificando hotspots como funções chamadas centenas de vezes por requisição.
Uma função chamada 50 vezes por request é candidata óbvia a cache. Porém, a sugestão algorítmica ignora semântica de domínio.
Considere uma função getUserPermissions() identificada como gargalo. A IA sugere cache com TTL de 5 minutos. A implementação reduz latência de 500ms para 300ms. Sucesso — até que usuários reportam que permissões revogadas continuam ativas por minutos.
O problema: permissões são controle de acesso. Qualquer inconsistência temporal é vulnerabilidade de segurança.
Em aplicações TypeScript, gargalos comuns incluem consultas repetitivas a banco de dados sem cache e falta de paralelização de I/O.
// ANTES: Chamadas sequenciais (gargalo identificado por IA)
async function getUserPermissions(userId: string): Promise<string[]> {
const roles = await db.roles.findByUserId(userId);
const permissions: string[] = [];
for (const role of roles) {
const rolePerms = await db.permissions.findByRoleId(role.id);
permissions.push(...rolePerms);
}
return [...new Set(permissions)];
}
// DEPOIS: Cache + paralelização (sugestão de IA)
const permissionCache = new Map<string, { perms: string[]; ts: number }>();
const CACHE_TTL = 5 * 60 * 1000;
async function getUserPermissionsCached(userId: string): Promise<string[]> {
const cached = permissionCache.get(userId);
if (cached && Date.now() - cached.ts < CACHE_TTL) return cached.perms;
const roles = await db.roles.findByUserId(userId);
const results = await Promise.all(
roles.map(role => db.permissions.findByRoleId(role.id))
);
const permissions = [...new Set(results.flat())];
permissionCache.set(userId, { perms: permissions, ts: Date.now() });
return permissions;
}
O problema que IA não vê: A otimização reduz latência de 500ms para 300ms, mas permissões alteradas demoram 5 minutos para refletir. Em sistema de controle de acesso, isso é vulnerabilidade de segurança. A IA analisa métricas quantitativas (50 calls/request = gargalo), mas não compreende requisitos qualitativos (revogação de permissão deve ser imediata) [8].
Trade-off Fundamental: Cache sempre troca consistência por performance. A decisão de usar cache em determinado contexto requer julgamento humano sobre o domínio.
Soluções mais sofisticadas incluem cache com invalidação ativa: quando permissões mudam, o sistema invalida entradas relacionadas. Isso preserva o ganho de performance sem sacrificar consistência.
Ferramentas Práticas: Ecossistema moderno
O ecossistema de ferramentas para refatoração assistida por IA inclui diferentes categorias:
Análise estática com IA: SonarQube, CodeQL e Snyk Code identificam code smells, vulnerabilidades e duplicações. Modelos de machine learning melhoram a precisão de detecção ao aprender padrões de codebases reais.
Assistentes de código: GitHub Copilot, Claude Code, Cursor e Aider sugerem refatorações inline. Ao selecionar um bloco de código e solicitar "refactor this for testability", o modelo gera alternativas que o desenvolvedor pode aceitar, modificar ou rejeitar.
Ferramentas especializadas: JetBrains oferece refatorações estruturadas (Extract Method, Rename, Move) com garantias de preservação de comportamento baseadas em análise de AST, não em heurísticas.
| Ferramenta | Foco Principal | Capacidades de IA | Integração |
|---|---|---|---|
| SonarQube + AI CodeFix | Qualidade, segurança, code smells | Sugestões de correção contextual, AI Code Assurance | CI/CD, IDEs [9][10] |
| GitHub Copilot Chat | Assistente de código no IDE | Sugere refatorações, explica código, gera testes | VS Code, JetBrains [11] |
| Amazon CodeWhisperer | Produtividade + stack AWS | Otimização de performance, geração de testes | IDEs, integração AWS [12] |
| Cursor AI | IDE AI-native | Refatoração contextual, edição multi-arquivo | IDE standalone |
SonarQube com AI CodeFix utiliza LLMs para gerar sugestões de correção contextual para issues detectadas, permitindo aplicar correções diretamente na IDE ou via branch dedicado. Esse modelo de "AI sugere, pipeline valida" aproxima a prática de refatoração de um fluxo de pull requests automatizados. GitHub Copilot Chat permite refatoração iterativa orientada a conversas: selecionar código, pedir "Refatore para melhorar coesão" e aplicar o patch sugerido [11].
A integração típica em workflow profissional combina análise estática em CI/CD (SonarQube detecta smells em pull requests) com assistência interativa durante desenvolvimento (Copilot sugere refatorações enquanto o desenvolvedor codifica).
Testes como Rede de Segurança: Coverage vs. Mutation Score
A refatoração pressupõe que o comportamento será preservado. Testes automatizados são a única forma de verificar essa premissa em escala. Porém, a métrica mais comum — code coverage — pode ser enganosa.
Coverage mede linhas executadas durante testes. Um teste que executa 100% do código mas não faz asserções relevantes atinge coverage máxima sem validar comportamento.
// Código com teste que dá 100% coverage mas não valida comportamento
function isEligibleForDiscount(age: number, yearsAsCustomer: number): boolean {
return age >= 65 || yearsAsCustomer >= 10;
}
// Teste superficial
test('eligible customer', () => {
expect(isEligibleForDiscount(70, 5)).toBe(true);
// ✅ Coverage: 100%
// ❌ Não testa: age = 65 exatamente, years = 10 exatamente
// ❌ Mutation "age > 65" passaria despercebida
});
Mutation testing oferece métrica mais rigorosa: introduz mutações no código (troca >= por >) e verifica se testes falham. Um sistema com 85% coverage mas 62% mutation score tem 23% do código "coberto" mas não efetivamente testado — refatoração nesse código pode introduzir bugs que passam despercebidos.
Um trabalho de 2024 propõe o RefactoringMirror, que usa LLMs apenas para identificar e especificar refatorações, mas delega a aplicação final a motores de refatoração tradicionais, obtendo taxa de reaplicação correta acima de 90% [3]. Isso reforça workflows híbridos: IA como consultora, ferramentas clássicas como executoras.
Refatoração em Larga Escala: Workflows Emergentes
Quando uma ferramenta identifica 500 code smells em um sistema de 200.000 linhas, a decisão de como proceder não é trivial. A abordagem "aplicar tudo automaticamente" economiza 200 horas de trabalho manual mas assume que testes são completos e que todas as sugestões são corretas.
Refatorar em larga escala significa operar em repositórios com centenas de milhares de linhas. Workflows emergentes seguem etapas como:
- Descoberta: SonarQube mapeia hotspots de technical debt; LLMs sintetizam relatórios
- Planejamento: Campanhas temáticas decompostas em PRs menores
- Aplicação assistida: Copilot/CodeWhisperer geram patches; revisão humana em pontos críticos
- Validação incremental: Testes em cada PR, monitoramento de métricas de qualidade
- Observabilidade pós-merge: Alertas específicos em módulos refatorados
Estudos sugerem que abordagens iterativas — múltiplas pequenas refatorações validadas — levam a resultados mais confiáveis que tentativas de "big bang refactoring" [13, 14].
Estratégia de categorização por risco: classificar refatorações como baixo risco (Rename onde convenção é clara), médio risco (Extract Method em código bem testado) e alto risco (Extract Class em código com baixo mutation score). Automatizar as primeiras, revisar as intermediárias, postergar as últimas até melhorar cobertura.
Estratégia incremental: aplicar refatorações em pequenos lotes, monitorar produção por alguns dias, prosseguir se estável. O custo é tempo; o benefício é detecção precoce de problemas.
Estratégia de staging prolongado: aplicar todas as refatorações em ambiente de staging, executar testes de carga e cenários de integração por uma semana antes de promover para produção.
Conclusão: Arquitetos Humanos, Ferramentas de IA
Usar IA para otimizar arquiteturas existentes é menos sobre substituir desenvolvedores e mais sobre reconfigurar o workflow de engenharia. LLMs são eficazes para refatorações localizadas quando integrados a análise estática; refatorações globais que impactam arquitetura continuam dependendo de julgamento humano.
Ou seja, ferramentas de IA tem o potencial de transformar a refatoração de arte solitária em processo industrial. A detecção automatizada de code smells elimina a dependência de memória e experiência individual. Sugestões de refatoração reduzem o esforço cognitivo de imaginar alternativas. Aplicação em larga escala torna viável modernizar sistemas legados massivos.
A convergência entre SonarQube AI, Copilot e CodeWhisperer mostra um futuro em que refatoração se torna atividade contínua e assistida. Isso levanta questões sobre responsabilidade: quem responde por uma otimização de cache sugerida por IA que viola requisitos de consistência? Como equilibrar indicadores de complexidade com conhecimento tácito da equipe?
Em resumo, a automação não substitui julgamento. A IA identifica que uma função é chamada 50 vezes por request; o humano decide se cache é apropriado dado o domínio. A IA detecta que uma classe tem 20 métodos; o humano avalia se a coesão é intencional (bounded context complexo) ou acidental (acúmulo histórico). A IA sugere 500 refatorações; o humano categoriza por risco e define estratégia de execução.
O princípio que atravessa todas essas decisões é invariável: testes são a rede de segurança. Sem eles, refatoração é roleta. Com eles — especialmente quando validados por mutation testing — refatoração assistida por IA torna-se ferramenta poderosa para combater dívida técnica e manter sistemas evolutíveis ao longo de décadas.
Por fim, o ponto central é: quais decisões delegar à IA e quais preservar como responsabilidade de arquitetos, reconhecendo que testes são fundamentais, mas não absolutos, na preservação de comportamento.
Referências
[1] Palomba et al. (2020). "On the diffuseness and the impact on maintainability of code smells." ICSM.
- Link: https://assets.ptidej.net/Publications/Documents/ICSM20.doc.pdf
- Por que ler: Estudo empírico que quantifica o impacto real de code smells na manutenibilidade — dados concretos para justificar investimento em refatoração.
[2] Liu et al. (2024). "An Empirical Study on the Potential of LLMs in Automated Software Refactoring." arXiv:2411.04444.
- Link: https://arxiv.org/abs/2411.04444
- Por que ler: Pesquisa mais recente (2024) sobre capacidades e limitações de LLMs em refatoração automatizada — benchmark com taxas de sucesso reais.
[3] Shirafuji et al. (2024). "RefactoringMirror: Enhancing LLM-based Refactoring." arXiv:2411.02320.
- Link: https://arxiv.org/abs/2411.02320
- Por que ler: Propõe abordagem híbrida (LLM especifica, motor tradicional aplica) com 90%+ de sucesso — workflow prático para equipes.
[4] Sharma & Spinellis (2020). "A Survey on Software Smells." JSS.
- Link: https://www.sciencedirect.com/science/article/abs/pii/S0164121220300881
- Por que ler: Survey abrangente que cataloga dezenas de code smells com métricas de detecção — referência para identificar smells específicos.
[5] Madeyski & Lewowski (2023). "Detecting Code Smells using ML." IST.
- Link: https://madeyski.e-informatyka.pl/download/JerzykMadeyski23.pdf
- Por que ler: Demonstra uso de machine learning para detecção de smells com acurácia competitiva — fundamento para ferramentas automatizadas.
[6] Pomian et al. (2025). "EM-Assist: LLM-based Extract Method Suggestions." ICSE.
- Link: https://seal-queensu.github.io/publications/pdf/IDE-Jonathan-2025.pdf
- Por que ler: Plugin de IDE que combina análise estática com LLM para Extract Method — exemplo prático de integração em workflow de desenvolvimento.
[7] Alomar et al. (2024). "LLMs for Code Refactoring: An Empirical Study." arXiv:2510.03914.
- Link: https://arxiv.org/abs/2510.03914
- Por que ler: Compara diferentes LLMs em tarefas de refatoração com métricas detalhadas — ajuda a escolher qual modelo usar para cada tipo de refatoração.
[8] Microsoft Research (2021). "The New Future of Work Report."
- Link: https://www.microsoft.com/en-us/research/wp-content/uploads/2021/01/NewFutureOfWorkReport.pdf
- Por que ler: Análise sobre impacto de ferramentas de IA em produtividade — contexto organizacional para adoção de assistentes de código.
[9] SonarSource (2024). "AI Features in SonarQube 10.7."
- Link: https://docs.sonarsource.com/sonarqube-server/10.7/user-guide/ai-features/
- Por que ler: Documentação oficial das capacidades de AI CodeFix — guia prático para configurar e usar em projetos reais.
[10] SonarSource (2024). "Sonar AI CodeFix Documentation."
- Link: https://www.sonarsource.com/company/press-releases/sonar-to-improve-the-quality-of-ai-generated-code-provide-automated-fix-recommendations/
- Por que ler: Anúncio oficial com casos de uso e limitações — entender o que a ferramenta pode e não pode fazer.
[11] GitHub (2024). "Refactor Code with Copilot."
- Link: https://docs.github.com/en/copilot/tutorials/refactor-code
- Por que ler: Tutorial oficial de refatoração com Copilot — passo a passo para aplicar em projetos TypeScript.
[12] AWS (2024). "Amazon CodeWhisperer Overview."
- Link: https://resources.learnquest.com/blog/amazon-codewhisperer-101-overview/
- Por que ler: Overview de capacidades e integração com stack AWS — útil para equipes que já usam ecossistema Amazon.
[13] Kim et al. (2014). "An Empirical Study of Refactoring Challenges at Microsoft." TSE.
- Link: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/kim-tse-2014.pdf
- Por que ler: Estudo clássico sobre desafios de refatoração em larga escala — lições aprendidas de empresa com milhões de linhas de código.
[14] Cassee et al. (2024). "Large-scale Refactoring in Practice." TOSEM.
- Link: https://dl.acm.org/doi/full/10.1145/3656429
- Por que ler: Análise de campanhas de refatoração massiva em empresas reais — workflows e métricas de sucesso aplicáveis.
[15] Cordeiro, J., Noei, S., & Zou, Y. (2024). An Empirical Study on the Code Refactoring Capability of Large Language Models. 1.
Top comments (0)