Critérios para design ruim

A vida funciona de forma bastante simples: muitas vezes, para ser inteligente, basta não fazer coisas estúpidas. Isso também se aplica ao desenvolvimento de software: na maioria dos casos, para fazer algo bem feito, basta não fazer mal.

A maioria dos programadores já teve experiência com partes do sistema que foram mal projetadas. Mas ainda mais triste, a maioria de vocês terá a triste experiência de perceber que foram os autores de tal sistema. Queríamos o melhor, mas acabou como sempre.

A maioria dos desenvolvedores não aspira a uma arquitetura ruim e, para muitos sistemas, chega um ponto em que começam a dizer que sua arquitetura é terrível. Por que isso está acontecendo? O design da arquitetura era ruim desde o início ou se tornou ruim com o tempo?

A raiz desse problema é a falta de uma definição de design “ruim”.

Parece-me que é a compreensão da qualidade do design e das razões da sua “decadência” que são as qualidades mais importantes para qualquer programador. Como na maioria dos outros casos, o principal é identificar o problema, e será uma questão de tecnologia resolvê-lo.

Definição de “design ruim”

Se você decidir se gabar de seu código na frente de um colega programador, provavelmente será ridicularizado como resposta: “Quem faz isso?”, 'Por que é assim?' e “Eu faria as coisas de maneira diferente”. Isso acontece com muita frequência.

Todas as pessoas são diferentes, mas você ainda escreve o código para seus colegas programadores; portanto, no processo de desenvolvimento de cada recurso, você sempre precisa de uma fase de revisão quando outras pessoas analisam seu código.

Mas mesmo que muitas coisas possam ser feitas de maneiras diferentes, há um conjunto de critérios com os quais todos os desenvolvedores concordam. Qualquer trecho de código que satisfaça seus requisitos, mas ainda exiba uma (ou mais) característica, é um projeto ruim.

Projeto Ruim:

  • Difícil de mudar porque qualquer mudança afeta muitas outras partes do sistema. ( Rigidez , Rigidez).
  • Quando são feitas alterações, outras partes do sistema quebram inesperadamente. ( Fragilidade , Fragilidade).
  • O código é difícil de reutilizar em outro aplicativo porque é muito difícil retirá-lo do aplicativo atual. ( Imobilidade , Imobilidade).

E o engraçado é que é quase impossível encontrar uma peça do sistema que não contenha nenhuma dessas características (ou seja, seja flexível, confiável e reutilizável), atenda ao requisito e, ao mesmo tempo, seu design seja ruim .

Assim, podemos usar essas três características para determinar inequivocamente se um projeto é “ruim” ou “bom”.

Causas do "Bad Design"

O que torna um design rígido, frágil e imóvel? Interdependência rígida de módulos.

Um projeto é rígido se não pode ser facilmente alterado. Essa rigidez se deve ao fato de que uma única alteração em um trecho de código em um sistema entrelaçado resulta em alterações em cascata em módulos dependentes. Isso sempre acontece quando uma pessoa está trabalhando no código.

Isso complica imediatamente todo o processo de desenvolvimento comercial: quando o número de mudanças em cascata não pode ser previsto pelo designer ou desenvolvedor, é impossível estimar o impacto de tal mudança. Portanto, eles tentam adiar essas mudanças indefinidamente.

E isso, por sua vez, torna o custo da mudança imprevisível. Diante de tal incerteza, os gerentes relutam em fazer mudanças, então o design torna-se oficialmente rígido.

Em algum momento, seu projeto ultrapassa o “horizonte de eventos” e está fadado a cair no “buraco negro” da arquitetura rígida.

Fragilidade é a tendência de um sistema de quebrar em vários lugares após uma única mudança. Normalmente, novos problemas ocorrem em locais que não estão conceitualmente relacionados ao local da mudança. Essa fragilidade prejudica seriamente a confiança no projeto e na manutenção do sistema.

Geralmente, esse era o caso quando não havia métodos privados. Basta tornar todos os métodos públicos e você estará condenado à aparência de uma arquitetura frágil. O encapsulamento ajuda a lidar com isso no nível micro. Mas no nível macro, você precisa de uma arquitetura modular.

Quando um projeto tem uma arquitetura frágil, os desenvolvedores não podem garantir a qualidade do produto.

Mudanças simples em uma parte do aplicativo levam a bugs em outras partes não relacionadas. Corrigir esses erros leva a ainda mais problemas, e o processo de escolta se transforma em um famoso cachorro perseguindo o próprio rabo.

O design é imóvel quando as partes necessárias do sistema estão fortemente ligadas a outros detalhes indesejados. Muito de seu próprio código, suas próprias abordagens e soluções exclusivas.

Você se lembra do registrador JUL, cujos desenvolvedores criaram seus próprios níveis de registro sem um bom motivo? Este é apenas o caso.

Para dar a um designer uma ideia de como é fácil reutilizar um design existente, basta pensar em como será fácil usá-lo em um novo aplicativo.

Se o projeto estiver fortemente acoplado, esse projetista ficará horrorizado com a quantidade de trabalho necessária para separar as partes necessárias do sistema dos detalhes desnecessários. Na maioria dos casos, esse design não é reutilizável, pois o custo de separá-lo supera o de desenvolvê-lo do zero.

Relevância

Tudo muda, mas tudo continua igual. (Provérbio chinês)

Perguntas muito boas foram levantadas acima. Quais são os perigos de sistemas frágeis e rígidos? Sim, porque o processo de gestão de tal projeto torna-se imprevisível e incontrolável. E o preço é exorbitante.

Como um gerente pode dar ou não sinal verde para adicionar algum recurso se ele não sabe quanto tempo isso realmente levará? Como priorizar tarefas se você não pode estimar adequadamente o tempo e a complexidade de sua implementação?

E como os desenvolvedores podem pagar a mesma dívida técnica quando vamos pagá-la e não podemos entender quanto arrecadaremos até que possamos arrecadar?

Problemas com reutilização ou teste de código também são muito relevantes. Os testes de unidade servem não apenas para testar algumas suposições sobre a unidade em teste, mas também para determinar o grau de sua coesão e podem servir como um indicador de reutilização.

Aqui está uma citação de Bob Martin para este caso: “Para reutilizar seu código, você precisa fazer um esforço de reutilizá-lo menor do que o custo de desenvolvimento do zero . ” Caso contrário, ninguém se incomodará com esse assunto.

O uso de princípios e padrões de design serve a um propósito - tornar o design bom. Se o uso deles não lhe traz nenhum benefício (ou vice-versa, viola os princípios do “bom design”), então algo em seu conservatório não está certo e, talvez, a ferramenta tenha começado a ser usada para outros fins.