archivo

Archivo de la etiqueta: simplicidad

Realizar una correcta gestión de las expectativas es fundamental en todo proyecto de desarrollo de software. Hay que ser muy constante en este aspecto porque aunque se piense que las expectativas están bajo control, las mismas van a variar con el tiempo, ya sea por la propia evolución del proyecto o por presiones que se reciban desde el exterior.

Los responsables funcionales te van a apretar porque tienden a exigirte un mayor alcance y en un menor tiempo de lo pactado. Tienen que comprender (y tener una cierta experiencia) cómo funciona un proyecto con prácticas de Scrum para que sean más moderados en sus peticiones de modificación del sprint, sin embargo, cuando ellos mismos están recibiendo presiones, les resulta complicado no traspasarte, aunque sea, parte de ellas.

Precisamente por este motivo se agiliza en muchos casos las prácticas de Scrum con Kanban, pero esto simplemente trata de definir determinadas reglas del juego, la complejidad realmente se encuentra en mantener un equilibrio entre lo que el usuario desea y lo que realmente se puede hacer (o lo que realmente se considera práctico realizar), porque está claro que el usuario manda, pero también debe ser consciente, y para eso estamos nosotros, de la complejidad que tiene la realización de una determinada solución.

Es importante poner las cartas sobre la mesa y a partir de ahí que decida. No vale con ser simplemente ejecutores de historias de usuario, también tenemos que proporcionar ese valor a los product owners porque se trata de aprovechar de la mejor manera posible la inversión, de manera que consigamos ganar el mayor valor posible con el menor esfuerzo y de no complicar de manera innecesaria el producto.

Un día sin escribir una línea de código puede ser más productivo que una semana picando código sin parar. Nunca el número de líneas, métodos o clases debe considerarse como métrica de avance del proyecto, al menos, mirándolo desde una perspectiva general.

El mejor código es aquel que nos ahorramos de escribir porque la funcionalidad no era necesaria, el enfoque no era bueno, existía una estrategia mejor, hemos podido reutilizar tal código o hemos encontrado una librería que hace justo lo que queríamos.

Partamos de la base de que va a ser prácticamente imposible que nuestro código tenga esos kilos de más entre otras cosas porque el desarrollo de software es evolutivo y lo que hoy parece una funcionalidad imprescindible mañana puede ser algo que no se utilice o que se tenga que rehacer por completo.

Teniendo presente eso, tenemos que tener en mente tanto nosotros como los responsables funcionales o products owners que la prueba y el error no es lá técnica a seguir (salvo en casos muy puntuales) sino que se debe desarrollar con intención con el objetivo de evitar la cómida rápida y que nuestro producto tenga una dieta equilibrada baja en calorías.

Para Bertrand Meyer: “El único gran enemigo de fiabilidad (y tal vez la calidad del software en general) es la complejidad”.

En mi opinión hay muchas más variables pero sí que es cierto que la complejidad condiciona, sin lugar a dudas, el resultado final de un producto, su usabilidad y la capacidad de realizar mantenimientos sobre él o, lo que es lo mismo, la capacidad de evolucionarlo.

Si nos dejamos seguir por las modas, si se imponen restricciones técnicas o de arquitectura que no condicionan sobre manera el proyecto, si miramos la solución como un juguete o un banco de pruebas, probablemente se le esté añadiendo la resultado final una complejidad innecesaria y que después va a resultar muy costosa de eliminar.

Si el producto se empieza a “engordar” con funcionalidades que no son necesarias, con módulos que no tienen nada que ver con su objeto final (porque resulta más barato incluirlos en la plataforma que crear un sistema desde cero para ellas), etc…, también se estará añadiendo complejidad que hay podido ser evitable.

A más complejidad más probabilidad de que existan errores y que no sean detectados. A más complejidad más deuda técnica.

Cuando vemos que bases de datos ofimáticas completamente desnormalizadas han dado servicio durante años a departamentos completos no terminamos de entenderlo. Tal vez lo vemos desde la perspectiva de la calidad del dato, de la explotación de la información para obtener conocimiento o desde la integración con terceros sistemas y por eso nos resulta complicado comprender cómo han podido trabajar así todo este tiempo.

Después sustituimos esa forma de gestión por sistemas más avanzados y nos damos cuenta de lo complicada que resulta su implantación, cuando no se convierte finalmente en un auténtico fracaso.

Y muchas veces no se tratan de malas decisiones de carácter funcional, sino de complejidad y falta de fluidez en el uso de la solución por parte de un usuario medio.

El estilo Nueva Jersey es una técnica de desarrollo de software (más bien una estrategia) basada en el principio “peor es mejor”, en la que se prefiere sacrificar determinados aspectos del producto final a cambio de conseguir un sistema mucho más sencillo de utilizar y de mantener (sobre todo esto último ya que permite adaptar de forma más rápida el sistema a las necesidades del usuario).

Este enfoque está fundamentado en el principio de que la mejor solución es la solución más simple que permite satisfacer las necesidades del usuario (menos es más). Muchas veces, el usuario o nosotros creamos necesidades que no lo son en realidad y complican el producto y el desarrollo de tal manera que lo hace más complicado de utilizar y más caro de mantener.

El estilo Nueva Jersey es simplemente una idea, para que no olvidemos que los sistemas complejos de ser necesarios deben proceder siempre de una evolución de un sistema más simple que haya sido aceptado por los usuarios.

La Ley de Gall se adapta perfectamente a los enfoques de desarrollo iterativo incrementales, si bien, y como os he comentado otras veces, la estrategia o metodología no sirve para nada si en la práctica no se toman decisiones efectivas.

Es decir, si el planteamiento del desarrollo no está basado en el intento de buscar las alternativas más simples que solucionen el problema y satisfagan las expectativas el usuario, nos encontraremos finalmente con un sistema más complejo del necesario, lo mismo si tratamos de construir la aplicación por el tejado sin haber trabajado previamente con funcionalidades que pueden ser igual o más prioritarias y en donde malas decisiones y/o implementaciones pueden condicionar sensiblemente el resto del sistema.

Es cierto que al ser un desarrollo de tipo evolutivo la complejidad se añade por incrementos y se tiene la oportunidad de rectificar a tiempo (el coste siempre será mucho menor que con el producto desarrollado de manera completa o con una buena parte de sus subsistemas ya desarrollados).

En un desarrollo en cascada el riesgo de caer en soluciones complejas que no han pasado previamente por un período de maduración basado en versiones más simples crece de manera exponencial, ya que este tipo de desarrollos tiene una vocación finalista que no es otra que la entrega del sistema completo (concepto llave en mano).

Su enunciado es el siguiente: “Un sistema complejo que funciona se encuentra de manera invariable como el resultado de un sistema simple que funcionaba. La proposición inversa también parece correcta: Un sistema complejo diseñado desde cero no funciona y no se conseguirá hacerlo funcionar. Tienes que empezar de nuevo con un sistema simple que funcione”.

John Gall, acierta de pleno en lo que al desarrollo de software se refiere. ¿Cuántos sistemas han fracasado por tratar de resolver el problema de una sola vez en lugar de tratar de hacerlo poco a poco?.

Después, tratar de darle la vuelta a la situación es complejo y costoso porque el sistema es muy grande, tendrá usuarios trabajando en él que se estarán continuamente quejando y que provocarán que los responsables funcionales centren las prioridades en resolver estos continuos incendios en lugar de buscar una solución general.

Este tipo de situación provoca un gran desgaste en usuarios y desarrolladores y se entra en un bucle de difícil solución porque cuando se plantee a los gestores que tal vez lo mejor sea empezar de nuevo o rehacer buena parte del sistema pondrán muchas resistencia y se negarán en la mayoría de los casos porque el coste les parecerá siempre excesivo (no tienen en cuenta el coste de mantener una solución no satisfactoria), teniendo en cuenta que acaban de realizar, no hace mucho una inversión muy fuerte.

Un amigo me comentó que efectivamente estaba de acuerdo que las aplicaciones tenían más complejidad funcional de la realmente necesaria y que en muchos casos hasta las funcionalidades más importantes no tenían la solución más simple lo que las hacía más pesadas al usuario (y provocaba que fueran más complejas de mantener) y afectaban a su productividad.

La complejidad se debe tratar a diferentes escalas:

Escala general: Sobre el conjunto del sistema. Reducir la complejidad implica dedicar tiempo a conocer el negocio y a entenderlo en la extensión que afecta al sistema de información. Tratar una parte concreta obviando el resto puede dar lugar (es lo más probable) a una solución más compleja. Pero, ¿cómo casa esto con un enfoque iterativo e incremental?, ¿cómo casa esto en un enfoque que se aleje del ciclo de vida clásico?.

Es perfectamente compatible solo que en los primeros ciclos el peso de tareas que ayuden a comprender el negocio será mayor que el de las dedicadas a programación. Dependiendo de la complejidad del negocio, del conocimiento que se tenga del mismo y de la actitud del área usuaria será mayor el número de ciclos en el que el peso de entender el negocio será mayor que el de la construcción.

Escala de detalle: Podemos haber trabajado en una solución simple a nivel general y después haber realizado una ejecución de la misma que la llene de complejidad. En este caso se trata de buscar la solución más simple para una funcionalidad concreta o (a un poco de más alto nivel), un módulo o un subsistema.