Ferramentas de IA estão transformando a forma como desenvolvemos software. Hoje é possível gerar controllers, services, testes, queries SQL e até funcionalidades completas em poucos minutos.
Ferramentas de IA estão transformando a forma como desenvolvemos software.
Hoje é possível gerar controllers, services, testes, queries SQL e até funcionalidades completas em poucos minutos.
Mas existe um problema:
Código rápido sem estrutura vira dívida técnica rápida.
A IA acelera a produção de código, mas não elimina a necessidade de uma arquitetura saudável.
Na prática, quanto mais código é gerado, mais importante se torna ter princípios que mantenham o projeto organizado, previsível e fácil de evoluir.
É exatamente nesse cenário que os princípios SOLID continuam extremamente relevantes.
Quando um projeto segue SOLID:
Quando o projeto não segue SOLID, a IA tende a amplificar os problemas existentes.
Ela pode até gerar a funcionalidade solicitada, mas normalmente seguirá os padrões já presentes no projeto.
Se a base estiver ruim, o resultado tende a ser ainda mais código difícil de manter.
Imagine um sistema para uma rede de padarias. Existe uma classe chamada:
PedidoManager
Ela faz tudo:
Um dia o time recebe uma solicitação simples:
"Adicionar um novo desconto para clientes VIP."
Parece uma tarefa de 30 minutos.
Mas ao alterar a lógica de desconto:
Por quê? Porque tudo está acoplado dentro da mesma classe.
Agora imagine outro cenário.
Um desenvolvedor precisa encontrar a funcionalidade de envio de e-mail. Naturalmente ele procura por:
Email
Notification
Mailer
Depois de uma hora procurando, descobre que a implementação está dentro de:
Financeiro/PedidoManager.cs
Misturada com cálculo de impostos.
Esse é exatamente o tipo de problema que SOLID ajuda a evitar.
Uma classe, uma responsabilidade.
Se existem dois motivos para mudar uma classe, provavelmente ela deveria ser dividida.
Estenda sem modificar.
Novo comportamento deve ser adicionado através de novas implementações, não alterando código que já funciona.
Uma subclasse deve poder substituir sua classe pai sem quebrar o sistema.
Se a implementação muda completamente o comportamento esperado, a herança está errada.
Interfaces pequenas e específicas.
Ninguém deveria implementar métodos que não utiliza.
Dependa de abstrações.
A regra de negócio não deveria saber se os dados estão em SQL Server, PostgreSQL ou MongoDB.
| Princípio | Exemplo |
|---|---|
| S | Um robô serve bebidas. Outro limpa mesas. |
| O | Novo tipo de bebida? Crie um novo robô sem alterar os existentes. |
| L | Qualquer robô garçom deve conseguir servir uma bebida sem comportamento inesperado. |
| I | O robô que limpa não precisa implementar "ServirBebida()". |
| D | O garçom não sabe onde a bebida é armazenada, apenas pede para alguém fornecer. |
public class UsuarioService
{
public void SalvarUsuario()
{
// salva usuário
}
public void EnviarEmailBoasVindas()
{
// envia email
}
}
A classe possui dois motivos para mudar:
public class UsuarioService
{
public void SalvarUsuario()
{
// salva usuário
}
}
public class EmailService
{
public void EnviarBoasVindas()
{
// envia email
}
}
Cada classe possui apenas uma responsabilidade.
public decimal CalcularDesconto(string tipoCliente)
{
if (tipoCliente == "Normal")
return 0;
if (tipoCliente == "VIP")
return 10;
if (tipoCliente == "Premium")
return 20;
return 0;
}
Toda vez que surge um novo tipo de cliente a função precisa ser alterada.
public interface IDesconto
{
decimal Calcular();
}
public class DescontoVip : IDesconto
{
public decimal Calcular() => 10;
}
public class DescontoPremium : IDesconto
{
public decimal Calcular() => 20;
}
Novo desconto? Nova classe. Sem alterar o código existente.
public class Passaro
{
public virtual void Voar()
{
}
}
public class Pinguim : Passaro
{
public override void Voar()
{
throw new Exception("Pinguim não voa");
}
}
Quem usa Passaro espera que ele voe.
Mas o pinguim quebra essa expectativa.
public abstract class Passaro
{
}
public interface IVoa
{
void Voar();
}
public class Aguia : Passaro, IVoa
{
public void Voar()
{
}
}
public class Pinguim : Passaro
{
}
Agora cada tipo possui apenas os comportamentos válidos.
public interface IFuncionario
{
void Trabalhar();
void GerenciarEquipe();
}
Um estagiário não gerencia equipe. Mesmo assim é obrigado a implementar.
public interface ITrabalhador
{
void Trabalhar();
}
public interface IGerente
{
void GerenciarEquipe();
}
Cada classe implementa apenas o que realmente utiliza.
public class PedidoService
{
private readonly SqlServerRepository _repository =
new SqlServerRepository();
public void Salvar()
{
_repository.Salvar();
}
}
A regra de negócio está presa ao SQL Server.
public interface IRepository
{
void Salvar();
}
public class SqlServerRepository : IRepository
{
public void Salvar()
{
}
}
public class PedidoService
{
private readonly IRepository _repository;
public PedidoService(IRepository repository)
{
_repository = repository;
}
public void Salvar()
{
_repository.Salvar();
}
}
Agora é possível trocar banco de dados sem alterar a regra de negócio.
Quando um projeto segue SOLID:
Isso não ajuda apenas os desenvolvedores.
Ajuda também as ferramentas de IA.
Um agente de IA consegue localizar mais facilmente onde uma alteração deve ser realizada quando a arquitetura é organizada.
Em projetos caóticos, a IA frequentemente replica problemas existentes, aumenta acoplamentos e cria mais dívida técnica.
SOLID nunca teve como objetivo tornar o código mais elegante. O objetivo sempre foi tornar mudanças mais seguras.
E isso se tornou ainda mais importante em uma era onde gerar código custa poucos segundos.
A velocidade de desenvolvimento aumentou. Mas a complexidade continua existindo.
Por isso, mais do que nunca:
Código gerado por IA sem SOLID é apenas uma forma mais rápida de criar problemas.
Já código estruturado com SOLID é mais fácil de entender, testar, revisar, evoluir e confiar.
E isso vale tanto para humanos quanto para as IAs que vão implementar a próxima feature.