archivo

Archivo de la etiqueta: complejidad

Comenta Bertrand Meyer que: “El único gran enemigo de fiabilidad (y tal vez de la calidad del software en general) es la complejidad”, y tiene gran parte de razón.

La complejidad en sus dos vertientes: incluir funcionalidades no necesarias (o hacer más complejas funcionalidades que sí son necesarias) y descuidar la arquitectura y construcción del software haciendo el software mucho más complicado de evolucionar (mayor esfuerzo y mayor probabilidad de errores).

Tenemos que aprender a ser pragmáticos, a dejar de construir la casa por el tejado, a querer rizar el rizo sin ni siquiera tener el producto funcionando y todo ello sin olvidar que si no se construye software de calidad estamos poniendo resistencia y frenos a la capacidad de adaptación y evolución del sistema.

Una solución más compleja no tiene por qué ser mejor, una solución con más funcionalidades no tiene por qué ser mejor y una solución que esté a lo último en tecnología no tiene por qué ser mejor. Lo de menos es más, no es tampoco ciencia exacta, pero la verdad es que funciona más veces de lo que creemos.

Si desarrollas una solución demasiado compleja, al final pasa como con los mandos a distancia, muchos botones, muchas funciones, pero al final no utilizas ni el 10% de ellas.

Por otro lado, añadir complejidad de manera sistemática sin tener el producto en producción supone prácticamente comprar toda la taquilla para un más que probable fracaso y con poco margen de maniobra posterior, ya que tendremos un software mucho más voluminoso que mantener (y con una mayor deuda técnica ) y habrá menos recursos económicos para hacerlo.

Para Gordon Bell (traducción libre): “Todo gran desastre computacional es consecuencia de tomar demasiadas ideas y ponerlas en un solo lugar”.

O lo que es lo mismo, consecuencia de buscar soluciones demasiado complejas sin pasar por otras soluciones intermedias más simples y/o sin pasar por un proceso previo de filtrado que trate, tal vez, de ser menos ambicioso en alcance a cambio de un enfoque más realista y efectivo.

Tener un amplio abanico de ideas donde elegir es una situación preferible a la escasez de las mismas pero siempre y cuando se gestionen con moderación, a eso se refiere Gordon Bell con su cita.

Si se toman muchas de ellas de una sola vez, sin reflexionar suficientemente sobre las mismas (o incluso haciendo esa reflexión) corremos el riesgo de desperdiciar esfuerzo ya que es posible que la hipótesis o visión de partida sobre determinados aspectos del producto sea equivocada y nos haga tirar una gran cantidad de trabajo que se podía haber evitado realizar si se hubiera optado por un desarrollo centrado en lo realmente relevante.

Resulta complicado que no lleguen defectos a producción. Llegado a un punto, la inversión a realizar para tratar de que el número de defectos que lleguen a producción sea cero, crecerá exponencialmente, si bien, no se trata solo de echar dinero, sino de hacer las cosas bien, y mucho mejor si se hacen desde un principio y están integradas como una parte más del proceso de desarrollo.

Cuanto mayor sea la complejidad del sistema, cuanta más deuda técnica exista, más posibilidades hay de surjan defectos y de que estos resulten, en ocasiones, complicados de detectar, porque en muchos casos serán consecuencia de una combinación de actuaciones del usuario que no ha sido contempladas o testeadas previamente.

¿Cuántas veces se ha tenido un cuidado extremo en tratar de que no se cuelen defectos y después, cuando el producto ha entrado en producción, han empezado a aparecer errores?

Por ese motivo, considero importante que los responsables funcionales y determinados usuarios empiecen a testear el producto (independientemente de la estrategia de testing utilizada por el equipo de desarrollo) desde la misma etapa de construcción, desde el mismo sprint, conforme se van completando historias de usuario. De esta forma, no solo se consigue perfilar mejor determinados aspectos de las historias, sino que también se permiten detectar defectos que probablemente hubieran llegado a producción.

Sobre lo comentado en este artículo, me parece muy interesante la siguiente cita de Brian Kernighan: “Cada nuevo usuario en un nuevo sistema descubre una nueva clase de bug”.

Niklaus With dijo (traducción libre): “Uno de los principales motivos de la complejidad es que los proveedores de software adopten sin crítica casi cualquier característica que los usuarios desean”.

Se puede adaptar la postura cómoda, que no quiere decir que sea sencilla, de limitarnos a recoger las especificaciones de los usuarios sin expresar una visión crítica.

Esto implica que nos guardaremos la opinión sobre la complejidad o simplicidad de la solución planteada, sobre la conveniencia de priorizar determinadas tareas sobre otras, en definitiva, no ponemos a disposición del product owner o del responsable funcional nuestro conocimiento y experiencia.

El desarrollo de software debe ser colaborativo porque las perspectivas, habilidades, conocimientos y experiencias del conjunto suman siempre más que cada una de ellas por separado.

¿Por qué adoptar esa posición? A veces, en función de la naturaleza del product owner, la confrontación de las opiniones puede originar desgaste y en cualquier caso siempre vas a trabajar más ante la necesidad de argumentar tus opiniones y de consensuar soluciones.

Sin embargo, en muchas ocasiones, la elección de esa postura es el resultado de arrojar la toalla como consecuencia de que el usuario ni quiere ni se deja asesorar.

El product owner debe definir la línea de desarrollo del producto, es su responsabilidad, pero también debe tener la capacidad de escuchar las recomendaciones de todo el conjunto de personas que colaboran en el proyecto, si deja esto de lado, probablemente la obtención de valor será más lenta y el producto resultante en cada iteración tendrá un nivel de complejidad mayor al necesario.

Los desarrolladores deben aportar, no solo su capacidad para desarrollar el software, sino también poner en valor su perspectiva y experiencias.

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.

Se tiende a añadir más y más funcionalidades al producto, pensando que de esta forma se le estarán dando al usuario más posibilidades, sin caer en la cuenta de la relación coste/beneficio de las mismas y que llegado el momento el usuario utiliza casi siempre las mismas.

Eso en el caso de que se piense en el usuario y no en incrementar la facturación a costa de eso (que es lo que suele ocurrir: crear necesidades donde no las hay).

Las nuevas funcionalidades visten más que otras tareas a realizar en el sistema y que son mucho más importantes, como rematar bien funcionalidades que sí que resultan esenciales, mejorar el rendimiento y la usabilidad o situar la deuda técnica en unos niveles acordes a las características del sistema.

Nuevas funcionalidades sí, siempre y cuando aporten valor, pero un valor real.

Sobre este tema Mary Poppendieck opina lo siguiente: “Nuestros sistemas software contienen más funcionalidades que las que se van a utilizar jamás. Esas funcionalidades extras incrementan la complejidad del código y elevan los costes de manera no lineal. Si ni siquiera la mitad de nuestro código es innecesario, el coste del sistema con le código adicional no es el doble, es por lo menos diez veces más caro de lo que debería ser”.

Con más funcionalidades se incrementa la deuda técnica, las mismas tendrán feedback y se realizarán ajustes, habrá errores que lleguen a producción y puedan tener influencia sobre otras funcionalidades más importantes y casi lo peor de todo: nuestra atención se desvía de lo realmente importante y se centra en lo accesorio, afectando al acabado y calidad del producto final.

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.