O DNA do Software Sólido

Ricardo Dias
Contexto Delimitado
8 min readAug 31, 2019

Sempre que um novo projeto se inicia, o entusiasmo toma conta da equipe envolvida e, na mente das pessoas, só há lugar para pensamentos positivos. A ideia de um novo software causa esperanças boas, com aquele pressentimento de que “dessa vez vamos fazer uma obra fantástica”.

Segundo BOOCH (2007), no desenvolvimento de software, “o principal desafio do gerenciamento é sempre manter a unidade e a integridade do design”.

Quando a programação começa, todos entendem o design inicial, todos falam dele nas reuniões, sugerem as melhores formas de aproveitar código e apresentam suas soluções para pequenos problemas. Tudo parece bem, a equipe se esforça para entregar um bom código, com indentação impecável e variáveis bem nomeadas.

Mas, aos poucos, a alegria começa a dar lugar para a preocupação. O design do software, que antes parecia maravilhoso, começa a ficar desajeitado e confuso. Pequenas alterações dão muito mais trabalho e a execução da aplicação começa a perder performance.

Esse cenário é mais recorrente do que se imagina!

No decorrer deste artigo, a palavra Design será usada, de forma que leitor deve entendê-la segundo a Engenharia de Software (ES). Na ES, quando falamos sobre o Design do Software, nos referimos a forma como o seu código foi arquitetado/projetado. Em outras palavras, dizer que “a forma como o código foi organizado é coesa e fácil”, é o mesmo que dizer “o Design é coeso e fácil”.

O DNA do software

Segundo Robert C. Martin, quando um software começa a se tornar muito difícil de dar manutenção, significa que ele tem problemas no seu Design. Neste sentido, o software em questão pode ser comparado a uma pessoa doente:

No começo não é tão ruim assim. Uma verruga feia aqui, um hack desajeitado ali, mas o design ainda apresenta alguma beleza. No entanto, com o tempo, à medida que a podridão continua, as feias feridas e as inflamações começam a se acumular até dominarem o design da aplicação. O programa se torna uma massa purulenta de código que os desenvolvedores consideram cada vez mais difícil de dar manutenção. Eventualmente, o esforço necessário para realizar até as mudanças mais simples na aplicação torna-se tão alto que os engenheiros e gerentes de linha de frente clamam por um projeto de reformulação. (MARTIN, 2000)

O hack desajeitado

Dentre as várias metáforas utilizadas por Martin, quero começar pelo “hack desajeitado”. Neste contexto, “um hack” significa uma coisa que está fora do lugar (ou que nem era para existir), pois foi colocada para alterar algum comportamento de forma que a “programação fosse mais rápida”, mesmo estando errada.

Nessa tratativa ingênua, geralmente ocasionada por causa da pressão de uma manutenção urgente, o desenvolvedor decide ignorar o Design do software e usar atalhos para resolver o problema.

Ao invés de entender a estrutura e a lógica de funcionamento do código para discernir qual seria o melhor lugar para colocar a nova tratativa de forma que o Design seja preservado, o desenvolvedor insere sua “gambiarra” em qualquer lugar que supostamente “dê certo”. Se funcionar, está resolvido… e a solidez do Design está perdida!

Quando um outro desenvolvedor precisar dar manutenção no mesmo código, terá dificuldade para entender a lógica, pois o código da regra de negócio estará desajeitado e espalhado em lugares distintos. O desenvolvedor será obrigado a usar alguma ferramenta externa de debug para entender a sequência de operações.

Na maioria das linguagens de programação, quando um código está realmente bem feito, não é necessário usar ferramentas externas para o debug de operações. Fica a dica!

O acúmulo de inflamações

De forma bem simplória, pode-se dizer que a inflamação é uma resposta do corpo humano para algum tecido morto presente no corpo. Suponhamos um órgão transplantado. Ele não pertence ao corpo e, a menos que um medicamento seja administrado, o sistema imunológico tentará expulsá-lo, gerando inflamações, apodrecimento e muito pus. Como exemplificado por Martin, se torna um “massa purulenta”.

Cada software possui um Design, uma forma de organização que o define e dá sentido (pelo menos no começo é assim…). Mesmo que essa forma não seja agradável ao desenvolvedor, ela representa o DNA do software. É responsabilidade dos engenheiros e do desenvolvedor zelar pela consistência deste Design, deixando tudo uniforme.

Uma situação muito comum acontece quando aquele desenvolvedor, apaixonado por Orientação a Objetos, é escalado para dar manutenção em um software com Design procedural. No código o que impera são estruturas e processos sequenciais e tudo é organizado assim. Mas o desenvolvedor não resiste a tentação, e decide “melhorar o paradigma”. Então, para implementar a manutenção, cria algumas “classezinhas” para conter seu código “Orientado a Objetos” e coloca elas lá no meio do Design, em algum lugar.

Lembra do “hack desajeitado”? Pois é, a situação que acebei de descrever é uma “versão profissional” dele. Agora não temos apenas um “hack desajeitado, temos um “Design desajeitado” que não faz sentido, pois Orientação a Objetos é muito mais que criar classes, é saber relacioná-las.

Quando outro desenvolvedor, acostumado com o Design procedural do software, precisar dar manutenção no mesmo código, ele terá muita carga mental para entendê-lo, pois o Design estará desajeitado, espalhado em paradigmas distintos, feio e difícil de entender, uma vez que se tornou uma “massa purulenta”.

Uma exceção seria, se os envolvidos no projeto se reunissem para elaborar uma estratégia de reformulação gradativa, onde o software, aos poucos fosse sendo migrado para o novo Design. Neste cenário a criação de classes seria plausível, embora igualmente custosa.

Uma segunda situação semelhante pode acontecer quando um desenvolvedor, acostumado com o programação procedural (mas que ainda não compreende muito bem a Orientação a Objetos) é escalado para dar manutenção em um software baseado em OO. Haverá um risco grande de o desenvolvedor gerar vários “hacks desajeitados”.

Nestes casos, metodologias como Extreme Programming (XP) são muito interessantes para fomentar a troca de experiências entre os envolvidos no desenvolvimento do software.

Por via de regra, o objetivo é não mudar o DNA do software e lutar para manter as células vivas e organizadas segundo sua natureza.

Diagnosticando um Design doente

O primeiro passo em direção a qualquer solução é ter consciência do problema. Um médico só pode receitar o melhor remédio para o paciente se antes ele conseguir fazer um diagnóstico adequado.

Quando a primeira versão do software é lançada, pouquíssimos problemas acontecem, pois, geralmente, o Design está de acordo com o que foi planejado. Os problemas começam a se alastrar na medida que as manutenções de evolução acontecem.

Para evitar grandes desastres, existem duas proficiências indispensáveis:

  • Saber identificar os problemas o quanto antes, aplicando em seguida uma medida corretiva para prevenir que o problema se alastre ainda mais;
  • Desenvolver o software de forma preventiva, evitando que os problemas aconteçam ou, se acontecerem, sejam fáceis de corrigir.

As duas qualidades são igualmente importantes e podem determinar o sucesso ou o fracasso de um software.

Identificando os problemas

Segundo MARTIN (2000) é possível identificar “quatro sintomas primários que revelam o estado de apodrecimento de nossos projetos”. A seguir, uma breve descrição sobre cada um deles.

Rigidez

Pode-se detectar que a Rigidez está tomando conta do software quando ele começa a ficar difícil de mudar. Mesmo mudanças simples obrigam uma série de alterações em outros módulos.

Quando a situação está intolerável, os donos do software começam a perder a confiança na equipe de desenvolvimento e no software, impedindo novas implementações por causa do custo alto para evoluí-lo. Nesse ponto a Rigidez se instala definitivamente.

Fragilidade

A Fragilidade começa a se instaurar quando a alteração em um módulo quebra a aplicação em várias outras áreas que não têm nada a ver com o que se está implementando.

A probabilidade de quebra vai aumentando gradativamente, tornando o software impossível de dar manutenção, pois cada correção piora a situação, gerando mais problemas do que solução.

Imobilidade

A Imobilidade é detectada quando os códigos gerados na implementação do software não podem ser reaproveitados em outros projetos, seja pelo Design pobre ou pela quantidade imensa de dependências associadas.

Segundo HOFMEISTER (2000), por pressão de tempo de desenvolvimento, os desenvolvedores constroem sistemas pouco modularizados ou com módulos muito acoplados, dificultando a manutenção.

Isso força a reimplementação da mesma funcionalidade, sempre do zero, para cada novo projeto. Cada reimplementação adquire um novo Design “inovador”, composto de novas dependências, mas continuando a não ser reaproveitável.

Viscosidade

A Viscosidade se torna alta quando é mais fácil fazer os “hacks desajeitados” do que uma implementação que preserve o Design. O código fica pegajoso e atraindo cada vez mais motivos para ignorar o Design. Em outras palavras, é fácil fazer a coisa errada, mas é difícil fazer a coisa certa.

Desenvolvimento preventivo

O famoso ditado popular já dizia que “é melhor prevenir do que remediar”. Na vida muitas doenças podem ser prevenidas com uma alimentação saudável e exercícios físicos.

O mesmo se aplica (mais uma vez) ao desenvolvimento de software. Ou seja, sabendo as melhores práticas, é possível prevenir os problemas.

Nos últimos 20 anos trabalhando com software, presenciei o sepultamento de vários projetos, que “faleceram” por causa de seu design arquitetural. Tudo parecia lindo no começo, mas em alguns anos os softwares se tornavam impossíveis de manter.

Após várias frustrações em empresas e projetos diferentes, encontrei, na literatura, pessoas que vivenciaram situações muito parecidas com as minhas e conseguiram transpor os problemas com soluções brilhantes.

Entre elas pode-se citar os famosos Design Patterns (ou Padrões de Projeto) e as metodologias como o Domain-Driven Design (DDD) e Test-Driven Development (TDD). A maioria desses conceitos e formas de criar software são baseados em princípios e boas práticas de desenvolvimento que começaram a surgir no começo deste milênio.

Esses princípios nasceram justamente para prevenir os problemas de design em softwares com mudanças constantes. Dentre os mais famosos, destacam-se os Princípios SOLID.

Conclusão

Neste artigo, tentei demonstrar alguns problemas recorrentes em softwares nos quais tive contato direto. Usei desta premissa com a finalidade de preparar o leitor para os próximos artigos, onde abordarei sobre os princípios SOLID, usados para a produção de softwares de sucesso no Paradigma de Orientação a Objetos.

Espero que o conteúdo esteja sendo útil. Até lá!

Leia também o próximo artigo sobre o assunto:

Leia todos os artigos desta série:

Referências para Aprofundamento

BOOCH, Grady; et all. Object-Oriented Analysis and Design with Applications. 3rd ed. Addison Wesley, 2007.

HOFMEISTER, Christine; et all. Applied Software Architecture, Addison Wesley, 2000.

MARTIN, Robert C. Design Principles and Design Patterns. 2000. Disponível em <https://sites.google.com/site/unclebobconsultingllc/home/articles>. Acessado em 31 de Agosto de 2019.

--

--

Ricardo Dias
Contexto Delimitado

Apaixonado por padrões, programação clara, elegante e principalmente manutenível. Trabalha como desenvolvedor deste 2000, incrementando a cada ano este loop…