La base para construir software bien hecho
16-05-2022
Las personas que buscan la satisfacción del trabajo bien hecho, que se ocupan de estructurar y diseñar el código lo mejor posible, a menudo se preguntan, ¿cómo puedo programar para dejar un software bien hecho?
Creo que no existe tal cosa, sobre todo porque hay muchas formas de resolver cada problema. El código tiene demasiados atributos como para reducir su calificación a bueno o malo. Algunos de los atributos deseables son:
- Que cumpla bien las necesidades de los usuarios, que les facilite el trabajo de verdad.
- Que cumpla con los requisitos funcionales y también los no funcionales.
- Que sea rentable para quien lo paga.
- Que el número de bugs o defectos sea cercano a cero.
- Que pueda adaptarse con facilidad a nuevas funcionalidades (rápido y barato de ampliar).
- Que pueda adaptarse a nuevos requisitos no funcionales (arquitectura flexible).
- Que pueda adaptarse a cambios en el negocio (rápido y barato de cambiar).
- Que sea rentable para quien lo construye.
El proceso para conseguir estas cualidades en el software es muy artesanal y requiere diferentes herramientas. En mi experiencia hay principios y prácticas que son fundamentales, mientras que luego hay técnicas que son interesantes o recomendables según el caso. Por orden de prioridad, para mí lo importante es que:
- Que los requisitos funcionales sean acertados y se cumplan.
- El código sea sostenible, lo cual significa que, como mínimo...
- Está cubierto por test automáticos.
- Los test son adecuados y sostenibles.
- Las abstracciones son adecuadas y tienen sentido.
- Hay una intención explícita con la que se escribe cada línea.
- La arquitectura es adecuada para cumplir los requisitos no funcionales del sistema.
- Cumple con los atributos de calidad (quality attributes).
- Define modelos de organización de las dependencias tales como la arquitectura hexagonal, CQRS, eventos y colas...
- Define el pipeline de construcción y despliegue del software.
- Utiliza patrones bien conocidos cuando se adaptan al problema:
- Frameworks y librerías de propósito general.
- Patrones del GoF, patrones GRASP, patrones de arquitectura para aplicaciones empresariales...
- Domain-driven Design (DDD)
Un código sostenible es el que elige la solución más simple para cada problema y se comprende instantáneamente al ojearlo sin tener que computar mentalmente. Es sostenible cuando se deja modificar y ampliar.
A menudo me encuentro repositorios de código con demasiada complejidad accidental (más complejidad de la necesaria), porque se centraron en el punto 4 de la lista y se olvidaron de los puntos 1, 2 y 3. Por supuesto que también los hay con exceso de complejidad y caos por haberse desarrollado sin ningún tipo de metodología ni criterio, con prisas, a salto de mata... ese tipo de situaciones es todavía muy común. Ahora bien, si te han hablado de las bondades de DDD es fácil que pienses que utilizar todas sus estrategias y tácticas en todos los proyectos, te garantizará un buen resultado. Si has leído un libro o hecho un curso, tendrás muchas ganas de aplicarlo. En mi opinión, DDD puede ser muy útil si el problema que tratas de resolver tiene un dominio rico, si estás construyendo el software con la cobertura de test adecuada y si estás cuidando la simplicidad. El problema de saltarte los puntos 2 y 3, es que vas a introducir mucha más complejidad de la necesaria en el proyecto. Al no tener test, será peligroso hacer cambios en el código y se irá volviendo rígido por la falta de refactorización constante. La complejidad accidental, junto con la dejadez, son las principales causas de la insostenibilidad del código.
Para resolver el punto 1, la toma de requisitos, hay muchas herramientas para entender el problema y descubrir las soluciones. En mi opinión, BDD (Behavior-driven Development) es una de las más valiosas y más infrautilizadas o infravaloradas. Si queremos desarrollar software util de verdad, tenemos que saber hacer un buen trabajo de análisis.
En cuanto al punto 2, el código sostenible está para mí en la base del desarrollo, representa el conjunto de principios elementales para poder cimentar una solución duradera. El demonio está en los detalles y el código no se arruina en un día, sino en semanas o meses de echar líneas sin tino. Saber cómo escribir cada línea para que se entienda requiere disciplina y cuidado, actitudes poco comunes en realidad.
El punto 3 también es clave para que el desarrollo sea satisfactorio, la arquitectura debe cumplir con los requisitos no funcionales. Por ejemplo, la solución no será segura, escalable o no tolerará fallos si no planteamos un diseño que tenga en cuenta estas premisas. El diseño evolutivo no resuelve cuestiones como la i18n o la l10n, es obligatorio plantear una arquitectura que soporte estos atributos de calidad (quality attributes) desde el principio.
Una vez que tenemos solvencia trabajo en los puntos 1, 2 y 3, tiene sentido ocuparnos de ver si podemos implementar partes de DDD, patrones de diseño... Con este artículo no quiero decir que no sea importante el punto 4, de hecho en nuestros proyectos a menudo usamos DDD y todos los patrones que sean convenientes. En realidad, todos los puntos de la lista están relacionados y a veces unos llevan a otros. Lo que quiero decir es que hay principios y técnicas que son más indispensables que otras, que en mi opinión deberíamos conocerlas muy bien y que ante la duda sobre cómo construir un "buen software", mi recomendación es ir a las bases. Un código simple, que se entiende bien y que está bien cubierto por test, ya es una base magnífica que la gran mayoría de los proyectos no tiene. Con cuidar de esa parte ya conseguirás una calidad fuera de lo común, muy por encima de la media. Todo lo demás añade valor cuando partimos de ese punto.
No existe el software perfecto, programar es un proceso artesanal y en todos los proyectos cometemos algún error, así que lo más importante es poderlos corregir de forma rápida y barata, más que pretender no cometer ninguno. Mi recomendación es complicarse lo menos posible, ir a lo fácil, a lo sencillo, lo intuitivo. Evitar meter patrones con calzador, añade los mínimos elementos necesarios para mantener el sistema lo más simple posible.