Revisemos algunas de las características principales de uno de los antipatrones más comunes en la industria del software para poder identificarlo y cambiar de dirección de manera controlada
problema
Estoy seguro que muchos de nosotros estamos familiarizados con el siguiente escenario:
Tenemos un conjunto de requerimientos que debemos atacar, y comenzamos a desarrollar porque no hay tiempo que perder
¿El diseño? - ya ira saliendo
¿Qué arquitectura o patrones utilizamos? - Esas cosas no nos sirven ahora, ya vendrá el tiempo de refactorizar
En fin, nos convencemos con una serie de pretextos y creemos ciegamente en las habilidades de distintos individuos para codificar y en el negocio para aclarar las dudas iniciales, pero como hemos visto en distintas publicaciones en este blog (gran spoiler), esto no será de mucha ayuda si no fijamos una dirección correcta en cada decisión

tipicas soluciones
"diseño" inicial
Comenzamos con el desarrollo. Tal como lo viste en los millones de tutoriales en línea. Creas un proyecto desde cero, te sientes confiado de utilizar las herramientas que te gustan o -peor- mejor aún, con las que quieres experimentar. Inicias con una estructura que tiene sentido y confías que el resto de las necesidades del código serán reveladas mágicamente en el futuro. Una vez alineadas las estrellas, el mensaje llegará a todos los miembros del equipo y sabrán qué hacer

Y así, como si todos los proyectos fueran iguales, comenzamos el desarrollo con un esqueleto que vimos en algún lado, sin alguna consideración directa de los requerimientos

interacción con el negocio
A medida que avanzamos en el proyecto, nos damos cuenta que el negocio no tiene las respuestas de muchas de nuestras preguntas. Lo que es aun peor, ni siquiera tienen claro de como evaluar la calidad de lo que estamos entregando

Es momento de cuestionar las decisiones de todos
iteraciones para empeorarlo todo
Aún no llevamos más de dos meses con el proyecto y comenzamos a sentir que ya es difícil realizar cambios en el código. Comenzamos a culpar al negocio y nuestros colegas, pero te das cuenta de algo, ni nuestro propio código es realmente flexible

Nos damos cuenta que:
- estamos cambiando mucho la firma de los métodos - hay que actualizar los llamados; hay que ajustar las pruebas, pero en este punto las pruebas ya no son representativas y mucho menos relevantes
- ya tenemos clases que extienden de otras clases solo para hacer uso de métodos que ni siquiera son públicos - piensas en como generalizarlo, pero el código está tan comprometido, que prefieres mirar a otro lado
- hay fragmentos de código parecidos, pero no exactamente iguales. Así que no nos tomamos la molestia de centralizarlo en un componente reutilizable
Y llegamos a ese punto, cuando no sabes exactamente cómo, pero terminamos en un gran charco de lodo del cual no podemos escapar. Hasta te convences de que el código nuevo ya va a estar bien. Pero recuerda, no tenemos un diseño que seguir, así que no tenemos una definición para medir lo que esta bien o no

recomendaciones
Si no tenemos una visión clara de lo que será el sistema, esta bien, aún hay patrones que nos ayudan con esto
código
SOLID
Sí, de nuevo. Particularmente los conceptos de inversión de control y uso de abstracciones. Ya hemos hablado antes de estos temas en el blog, puedes encontrar aquí una introducción práctica a estas prácticas
En resumen, estas prácticas te ayudarán a segregar todo y mantener tus componentes flexibles para cambios
arquitecturas
microkernel
En mi experiencia, es muy sencillo iniciar con esta arquitectura. Puedes encontrar información detallada aquí. En pocas palabras, permite definir componentes abstractos (interfaces) y sus implementaciones al estilo de plugins. Aunque inmediatamente nos damos cuenta que esto es un sistema monolítico, tiene la ventaja de que, teniendo las interfaces propiamente definidas, podemos sustituirlos por llamados a microservicios en caso de requerirlo en el futuro siguiendo el patrón de strangler
microservicios
En caso de ser posible, podemos dividir nuestros módulos por contexto (usuarios, cobranza, reservaciones, etc.), y cada uno de estos módulos se convertirá un microservicio. Hay que tener en mente que, aunque microservicios provea una forma sencilla para separar nuestro software, existen costos inmediatos que podrían perjudicar al equipo: monitoreo, mantenimiento, capacitación, erosión en la arquitectura, homogenizar las buenas prácticas, construcción de pipelines para despliegues, etc. Por estas razones, considero que sería más seguro para un equipo bajo presión comenzar con monolito y después desacoplar los distintos servicios
conclusiones
Es necesario fijar un rumbo en el proyecto a todos los niveles, el código no es ninguna excepción. Si tenemos una arquitectura a seguir, el diseño de esta arquitectura nos indicará los atributos y valores que vuelven a nuestra solución de buena calidad
Existen patrones, arquitecturas y principios que nos ayudan a tomar decisiones de forma agnóstica cuando contamos con poca información o tiempo para darle sentido a nuestro código. Tomar estas decisiones no lleva tanto tiempo y los beneficios en el proyecto se ven reflejados en la facilidad para realizar cambios y la tranquilidad de los integrantes del equipo
