Criterios para un mal diseño

La vida funciona de manera bastante simple: a menudo, para ser inteligente, simplemente no necesitas hacer cosas estúpidas. Esto también se aplica al desarrollo de software: en la mayoría de los casos, para hacer algo bien, solo necesitas no hacerlo mal.

La mayoría de los programadores han tenido experiencia con partes del sistema que estaban mal diseñadas. Pero aún más triste, la mayoría de ustedes tendrá la triste experiencia de darse cuenta de que fueron los autores de tal sistema. Queríamos lo mejor, pero resultó como siempre.

La mayoría de los desarrolladores no aspiran a una mala arquitectura, y para muchos sistemas llega un punto en el que empiezan a decir que su arquitectura es pésima. ¿Por qué está pasando esto? ¿Fue malo el diseño de la arquitectura desde el principio, o se ha vuelto malo con el tiempo?

La raíz de este problema es la falta de una definición de “mal” diseño.

Me parece que la comprensión de la calidad del diseño y las razones de su "deterioro" son las cualidades más importantes para cualquier programador. Como en la mayoría de los otros casos, lo principal es identificar el problema, y ​​será cuestión de tecnología solucionarlo.

Definición de “mal diseño”

Si decide alardear de su código frente a un compañero programador, lo más probable es que lo ridiculicen como respuesta: "¿Quién hace esto?", "¿Por qué es así?" y “Haría las cosas de otra manera”. Esto sucede muy a menudo.

Todas las personas son diferentes, pero aún escribe el código para sus compañeros programadores, por lo que en el proceso de desarrollo de cada función, siempre necesita una fase de revisión cuando otras personas miran su código.

Pero incluso si se pueden hacer muchas cosas de diferentes maneras, hay un conjunto de criterios en los que todos los desarrolladores estarían de acuerdo. Cualquier pieza de código que satisfaga sus requisitos pero aún muestre una (o más) características es un mal diseño.

Mal diseño:

  • Difícil de cambiar porque cualquier cambio afecta a muchas otras partes del sistema. ( Rigidez , Rigidez).
  • Cuando se realizan cambios, otras partes del sistema se rompen inesperadamente. ( Fragilidad , Fragilidad).
  • El código es difícil de reutilizar en otra aplicación porque es muy difícil sacarlo de la aplicación actual. ( Inamovilidad , Inmovilidad).

Y lo curioso es que es casi imposible encontrar una pieza del sistema que no contenga ninguna de estas características (es decir, que sea flexible, fiable y reutilizable), cumpla con el requisito, y al mismo tiempo su diseño sea malo. .

Por lo tanto, podemos usar estas tres características para determinar sin ambigüedades si un diseño es "malo" o "bueno".

Causas del "mal diseño"

¿Qué hace que un diseño sea rígido, frágil e inamovible? Rígida interdependencia de módulos.

Un diseño es rígido si no se puede cambiar fácilmente. Esta rigidez se debe al hecho de que un solo cambio en una pieza de código en un sistema tejido da como resultado cambios en cascada en los módulos dependientes. Esto siempre sucede cuando una persona está trabajando en el código.

Esto complica inmediatamente todo el proceso de desarrollo comercial: cuando el diseñador o desarrollador no puede predecir el número de cambios en cascada, es imposible estimar el impacto de tal cambio. Por lo tanto, intentan posponer tales cambios indefinidamente.

Y esto, a su vez, hace que el costo del cambio sea impredecible. Ante tal incertidumbre, los directivos se resisten a realizar cambios, por lo que el diseño se vuelve oficialmente rígido.

En algún momento, su proyecto pasa el "horizonte de eventos" y está condenado a caer en el "agujero negro" de la arquitectura rígida.

La fragilidad es la tendencia de un sistema a fallar en múltiples lugares después de un solo cambio. Por lo general, los nuevos problemas ocurren en lugares que no están conceptualmente relacionados con el lugar del cambio. Tal fragilidad socava seriamente la confianza en el diseño y mantenimiento del sistema.

Este solía ser el caso cuando no había métodos privados. Basta con hacer públicos todos los métodos y estará condenado a la apariencia de una arquitectura frágil. La encapsulación ayuda a lidiar con esto a nivel micro. Pero a nivel macro, necesitas una arquitectura modular.

Cuando un proyecto tiene una arquitectura frágil, los desarrolladores no pueden garantizar la calidad del producto.

Los cambios simples en una parte de la aplicación generan errores en otras partes no relacionadas. Corregir estos errores genera aún más problemas, y el proceso de escolta se convierte en un famoso perro que se muerde la cola.

El diseño es inmóvil cuando las partes necesarias del sistema están fuertemente ligadas a otros detalles no deseados. Demasiado de su propio código, sus propios enfoques y soluciones únicos.

¿Recuerdas el registrador JUL, cuyos desarrolladores crearon sus propios niveles de registro sin una buena razón? Este es solo el caso.

Para darle a un diseñador una idea de lo fácil que es reutilizar un diseño existente, basta con pensar en lo fácil que será utilizarlo en una nueva aplicación.

Si el diseño está estrechamente acoplado, este diseñador se horrorizará por la cantidad de trabajo necesario para separar las partes necesarias del sistema de los detalles innecesarios. En la mayoría de los casos, dicho diseño no es reutilizable, ya que el costo de separarlo supera el de desarrollarlo desde cero.

Relevancia

Todo cambia, pero todo sigue igual. (Proverbio chino)

Arriba se han planteado muy buenas preguntas. ¿Cuáles son los peligros de los sistemas frágiles y rígidos? Sí, porque el proceso de gestión de un proyecto de este tipo se vuelve impredecible e incontrolable. Y el precio es desorbitado.

¿Cómo puede un gerente dar o no el visto bueno para agregar alguna característica si no sabe cuánto tiempo llevará realmente? ¿Cómo priorizar tareas si no puede estimar adecuadamente el tiempo y la complejidad de su implementación?

¿Y cómo pueden los desarrolladores pagar la misma deuda técnica cuando cobraremos al pagarla y no podemos entender cuánto cobraremos hasta que lo hagamos?

Los problemas con la reutilización o las pruebas de código también son muy relevantes. Las pruebas unitarias sirven no solo para probar algunas suposiciones sobre la unidad bajo prueba, sino también para determinar el grado de su cohesión y pueden servir como indicador de reutilización.

Aquí hay una cita de Bob Martin para este caso: "Para reutilizar su código, debe hacer el esfuerzo de reutilizarlo menos que el costo de desarrollar desde cero " . De lo contrario, nadie se molestará con este asunto.

El uso de principios y patrones de diseño tiene un propósito: hacer que el diseño sea bueno. Si su uso no te reporta ningún beneficio (o viceversa, vulnera los principios del “buen diseño”), entonces algo en tu conservatorio no anda bien y, quizás, la herramienta ha comenzado a utilizarse para otros fines.