DEV Community

Thomas Lincoln
Thomas Lincoln

Posted on

Modularidade: Por que você deve parar de escrever arquivos de 600 linhas

“Modularidade é um princípio organizacional” - Fundamentos da Arquitetura de Software

“Noventa e cinco por cento das palavras [sobre arquitetura de software] são usadas enaltecendo os benefícios da “modularidade” e poucas, se algumas, tratam de como alcançá-la. - Glenford J. Myers (1978)

Recentemente, terminei a minha leitura do livro Fundamentos da Arquitetura de Software: Uma abordagem de engenharia de Mark Richards e Neal Ford. Muitos assuntos foram tão interessantes que me impulsionaram a tentar compartilhar com outras pessoas em uma outra linguagem, claro, o meu texto não tem como objetivo de substituir a leitura desse incrível livro, apenas de apresentar esse tópico para mais pessoas. Inclusive, incentivo completamente a leitura do livro, onde os autores provavelmente irão se aprofundar muito mais no tema.

Para começar essa conversa, eu gostaria de definir o que é modularidade. A ideia toda consiste em dividir nossos sistemas em módulos, cada módulo deve ser independente e ter apenas uma responsabilidade (provavelmente você já deve ter visto isso se leu sobre código limpo). Mas, okay, apenas isso não parece tão óbvio, então vamos com um exemplo.

Aqui nós temos um código todo misturado, um código “espaguete”:

# Tudo em um único bloco
preco = 100.0
desconto = 0.1

if preco > 0:
    preco_final = preco * (1 - desconto)
    print(f"Salvando no banco de dados...")
    print(f"Produto processado. Valor final: R$ {preco_final}")
else:
    print("Erro: Preço inválido")
Enter fullscreen mode Exit fullscreen mode

Como vocês podem ver, (abstraia um pouco os prints como funções kk), o código está todo junto, as operações são executadas todas de uma vez, se quisermos fazer uma alteração em algo, precisamos alterar justamente nesse código. Se acontecer um erro, precisamos analisar o código todo, e sinceramente, não sei você leitor, mas eu não iria gostar de ter que testar um código todo sempre que algo der errado.

Agora olhe essa outra versão:

def calcular_desconto(valor, taxa):
    """Módulo de Lógica de Negócio"""
    return valor * (1 - taxa)

def salvar_no_banco(mensagem):
    """Módulo de Persistência (Simulado)"""
    print(f"DB: {mensagem}")

def processar_venda(preco, taxa_desconto):
    """Módulo Orquestrador"""
    if preco <= 0:
        print("Erro: Preço inválido")
        return

    valor_final = calcular_desconto(preco, taxa_desconto)
    salvar_no_banco(f"Venda realizada. Total: R$ {valor_final}")

# Execução
processar_venda(100.0, 0.1)
Enter fullscreen mode Exit fullscreen mode

Observe que, caso tenhamos um problema com o cálculo de desconto vindo errado, precisamos mexer apenas em um método, se o registro não estiver sendo salvo no banco, apenas um método também.

Claro, você pode me dizer, mas Thomas, o outro código era tão pequeno, obviamente era possível ajustar ele. Mas ai é que entra o problema, nem todo código é pequeno, já me ocorreu de no meu trabalho, ter de lidar com um código (que eu mesmo fiz 🙂) que tinha apenas 600 linhas.


Por que queremos essa modularidade?

Na era das LLMs (como ChatGPT e Copilot), a modularidade se tornou nossa melhor defesa. A IA é excelente em gerar funções isoladas, mas muitas vezes se perde em contextos gigantescos. Se o seu código é modular, você consegue usar a IA de forma cirúrgica para corrigir ou melhorar peças específicas, sem que ela 'alucine' e quebre o resto do sistema.

Um segundo ponto bem importante, o qual eu citei previamente, é o fato de aumentar a facilidade de debug, com um código modularizado, nós conseguimos identificar melhor em qual parte do nosso código nós temos um problemas, em um código monolítico de 600 linhas, pode ser complicado voltar depois de alguns dias e entender o que aconteceu.

Por fim, modularizar seu projeto te permite reutilizar seus códigos, caso você tenha uma função de importação de dados para o banco que precise ser usada em duas partes da sua aplicação, você pode simplesmente chamar o método em outro lugar, sem precisar reescrever tudo do zero.


Existem trade-offs?

Como toda decisão em arquitetura, existem alguns trade-offs, que devem ser considerados antes de utilizar modularização, afinal, ela não é uma bala de prata. Caso você modularize demais, você chega no que nós chamamos de “Over-engineering”.

Com isso, primeiramente vamos ter um aumento considerável na complexidade cognitiva. Embora cada módulo isoladamente possa ser simples, o sistema como um todo pode acabar se tornando tão cheio de módulos, que o problema de um código gigante que você resolveu, é substituído por um problema de entender como os 100 módulos se comunicam e executam a tarefa toda.

Nós também precisamos ter em mente que esses módulos precisam se comunicar entre si, isso gera um aumento das chamadas de função e gerenciamento de memória, caso você esteja trabalhando com sistemas distribuídos, o impacto aumenta muito, pois a comunicação entre os módulos é feita usando uma rede com latência e outros problemas.

Entre outros problemas que vou dissecar melhor em outros posts, mas podemos resumir os problemas em:

  • Fragmentação excessiva, muitos módulos dificultam a manutenção.
  • Velocidade inicial mais lenta, caso essa modularidade seja feita de forma descoordenada com a arquitetura do projeto, ela pode gerar uma lentidão maior.
  • Difícil rastrear o fluxo entre os módulos.

E você, já teve que dar manutenção em um código que parecia um novelo de lã? No próximo post, vou falar sobre como alcançar essa modularidade na prática.

Top comments (0)