Archivo

Archivo de la etiqueta: deuda técnica

La sobreproducción se entiende como la realización de trabajo de más que no resulta necesario en estos momentos. Tiene las siguientes implicaciones:

- Esfuerzo realizado que tal vez no sirva para nada, ya que una vez que el cliente evalúe lo que realmente necesita puede modificar total o parcialmente todo el conjunto de funcionalidades de más que se han realizado.

- División de la atención entre lo realmente importante y lo que no lo es. Ese esfuerzo que se dedica a lo que no es prioritario puede afectar a la calidad, al grado de terminación y a la tasa de errores de lo que sí lo es.

- El usuario, cuando se le presenta la solución en su conjunto (en su versión actual), también pierde el enfoque pudiéndose perderse en detalles que están lejos del objetivo principal de la versión.

- Incremento de la complejidad del sistema tanto a nivel técnico (deuda técnica) como de la usabilidad.

Trabajo de más es resistencia de más para poder adaptarnos al cambio, ya que nos resta flexibilidad. A lo anterior hay que sumarle el coste que se ha invertido en desarrollar funcionalidades que lo mismo nunca se llegan a utilizar o deben ser sensiblemente reajustadas, por lo que disminuye la proporción entre valor y esfuerzo.

Trabajamos para eso, para desarrollar software que funciona. Pero, ¿de qué se trata eso de que el software funcione?. Cada uno tenemos diferentes perspectivas de las cosas, esta no iba a ser una excepción, por lo que para cada uno el listón puede estar a diferente altura y no necesariamente (dependerá del momento del proyecto, del contexto, de las características del sistema de información, etc…) unos tienen que estar siempre equivocados y otros estar en lo cierto.

Siguiendo un enfoque iterativo incremental vamos a ir liberando diferentes versiones del producto hasta obtener una “versión final” (lo pongo entre comillas porque el software es generalmente objeto de una continua evolución por lo que se puede hablar de versiones finalistas de un proyecto más que versiones finales de un sistema de información).

Independientemente de que desde el primer momento intentemos liberar el mejor software posible (desarrollo con intención) es razonable pensar que en las primeras iteraciones de una aplicación el listón no esté tan alto, sobre todo en aquellos casos donde esas iteraciones no llegan a un entorno de producción o de llegar su uso está limitado a su evaluación y porque la incertidumbre y falta de acoplamiento (al proyecto y entre las personas) en los momentos iniciales hace que se falle más de la cuenta (desarrolladores y usuarios).

No se trata de relajarnos en las primeras iteraciones, no quiero decir eso, sino de entender que no todos los momentos del proyecto son iguales (ni todas las circunstancias). No hay minutos prescindibles en un proyecto porque lo perdido no se recupera y por ese motivo siempre soy partidario de ir con intención siempre sin olvidar y, eso es lo que quiero dejar patente, que como en toda carrera de larga distancia hay que saber regular (y que todas las carreras son diferentes).

Un software que funciona debe satisfacer las expectativas del usuario. Si no satisface sus expectativas tenemos un producto que hace cosas pero no tal y como las quiere el usuario. No se trata de términos absolutos sino que tenemos que tener en cuenta los umbrales, es decir, la liberación de una nueva versión puede que no deje totalmente satisfecho al usuario pero sí supere sus umbrales de satisfacción. Nuestro objetivo será que la “versión final” esté más cerca de la total satisfacción del usuario que de su umbral superior pero eso requerirá mucho trabajo, voluntad por parte de los usuarios para darnos su feedback y diferentes evoluciones del sistema.

Un software que funciona no debe quedarse solo con la parte visible del iceberg. La deuda técnica cuenta y mucho. El usuario no la ve, no la valorará y sin embargo condicionará la mantenibilidad del producto y la disponibilidad del sistema ante futuras evoluciones del mismo. Es posible que haya clientes que no la tengan en cuenta, en cualquier caso soy de la opinión de que los proveedores deben marcar unos estándares de calidad para los sistemas que desarrollan, como elemento diferencial respecto a los que no lo hacen.

Más allá de la deuda técnica se encuentra la mantenibilidad (un concepto más general). Un software que funciona debe ser lo más fácilmente de mantener posible (dentro del contexto en el que se ha realizado el proyecto) y eso va más allá de la deuda técnica pudiendo contemplar elementos documentales si así fuera preciso.

Un software que funciona debe tener también en cuenta aspectos no funcionales. Algunos de ellos están en la parte visible del iceberg (aunque tal vez en la parte de atrás, la que no se ve a simple vista o la que requiere más tiempo para ser descubierta) como por ejemplo el rendimiento o la disponibilidad y otros en la parte no visible como por ejemplo la seguridad.

Se entiende que un equipo de proyecto irá incrementando la velocidad de desarrollo (volumen de trabajo que se puede afrontar en una iteración) hasta alcanzar una cierta estabilidad tras una serie de sprints (cambios en el equipo de proyecto ya sea de personas, en número, etc… afectarán a la velocidad, también cambios drásticos relacionados con el contexto de trabajo: tecnologías, entornos, metodologías, etc…).

Es bastante razonable que eso sea así salvo que la deuda técnica ofrezca cada vez una mayor resistencia.

Todo esto es teoría, si bien es la situación que se puede dar con mayor probabilidad.

Pero, ¿cómo medimos objetivamente el incremento de velocidad? Es decir, notaremos que el equipo puede afrontar una mayor cantidad de trabajo porque estimarán en menos tiempo el desarrollo o modificación de las pantallas, de la realización de informes, etc…, ¿pero cuánto realmente es esa mejora?, ¿en qué lo medimos?, ¿vale la pena el esfuerzo necesario para medirlo?.

Ahí estriba la dificultad porque lo ideal sería traducir los esfuerzos necesarios a una únidad genérica común, lo que en algunas metodologías se denominan puntos función, puntos por caso de uso, puntos por historia de usuario, etc… Esa traducción tiene un alto componente de subjetividad y se requerirá tiempo de perfeccionamiento conseguir una traducción con una cierta fiabilidad a esa unidad genérica común.

¿Invertimos esfuerzo en ello? Depende de en la fase en la que nos encontremos en la implantación de estrategias de desarrollo ágiles, si todavía no se tiene madurez (y para que exista se requiere tiempo) es mejor esperar a que se tenga una cierta estabilidad en la aplicación de estas dinámicas de trabajo de manera que el equipo tenga ya asimilados ciertos automatismos, una vez hecho eso, puede ser interesante empezar a medir.

No obstante, una cosa es medir en base a unidades genéricas y otra que no se haga un seguimiento de la velocidad en el sprint ya que resulta importante conocer (y eso se puede hacer con un esfuerzo mínimo) si el número de días de trabajo estimado para terminar los trabajos es congruente con la fecha de finalización del sprint por si fuera necesario (o posible) tomar alguna medida que permita reconducir desviaciones y/o gestionar las expectativas.

Sistemas altamente acoplados, con clases y métodos kilométricos, con código que resulta complicado de entender son ejemplos en donde realmente se programa con miedo una vez que se comprueba que tocarlo supone implicaciones hasta en las partes más insospechadas de la aplicación.

Si el sistema además es crítico el miedo pasa a convertirse en pánico.

Tal vez las palabras miedo o pánico sean demasiado exageradas (y no es nada positivo trabajar de esa manera) pero por lo menos el programador debe sentir respeto y estar alerta cuando realiza modificaciones en un sistema de este tipo. El exceso de confianza no es bueno y más en un caso como este.

Hay que evaluar si el cambio que se va a hacer en el sistema es algo puntual o si se sabe que van a existir una serie de cambios a corto y medio plazo, así como la magnitud de los mismos y la magnitud del sistema. El esfuerzo que supone realizar estos cambios (mucho mayor que en un sistema con una deuda técnica ajustada a sus características), el esfuerzo en testing y el riesgo que tiene liberar nuevas versiones de estos sistemas debe ser analizado antes de afrontar la mejor estrategia para el cambio.

En condiciones como esta, cambios que sean fruto de un capricho no deben ser contemplados y cambios muy relevantes que supongan una modificación sustancial del sistema podrían dar lugar a que se tome la decisión de desarrollar una nueva aplicación.

El programador por su parte puede tomar sus precauciones: testing unitario, automatización de determinado testing de mayor nivel, etc…

Por otro lado, el paso a producción de nuevas versiones de sistemas de estas características no debe tratarse a la ligera y esperar cruzando los dedos a que no pase nada.

El testing debe ser realizado en profundidad y desde el punto de vista funcional debería ser la combinación de casos de prueba y testing exploratorio.

Cuando la deuda técnica de un componente impide un desarrollo fluído ya sea porque se requiere un mayor esfuerzo y/o porque la posibilidad de que se produzcan efectos colaterales es muy alta o cuando el usuario advierte que el producto tiene alguna deficiencia funcional en un aspecto fundamental de su funcionamiento es el momento de plantearse el dar una solución a esos problemas antes de continuar con la evolución del producto.

Huir hacia adelante no suele funcionar bien en el desarrollo de software porque el problema no se elimina de esa forma y por el coste que tiene arrastrar con esa deuda técnica y/o esas deficiencias.

Desde mi punto de vista esa decisión no debe ser tomada unilateralmente por el desarrollador sino que debe ser consensuada con el responsable funcional, product owner o como queremos denominarlo porque es responsabilidad del mismo definir la línea de desarrollo del producto y, si vamos a realizar acciones que van a ralentizar la evolución del producto (a corto plazo), debe ser consciente de ello y autorizarlo.

Si la tarea de refactorización puede desarrollarse dentro de la iteración se consideraría como una tarea más de la misma y se ejecutaría sin más (avisar o no al product owner dependería de las políticas que, al respecto, se tuvieran definidas en el proyecto).

Siempre es posible desarrollar un software aplicando las mismas técnicas de gestión que para construir un puente, sin embargo estaríamos prescindiendo de una de sus principales características, su maleabilidad, lo que le permite adaptarse al cambio y modificar criterios con un coste asumible (siempre y cuando se tenga la deuda técnica bajo control).

Cuando tienes un puente a medio construir, tomar la decisión de poner un carril más en cada dirección puede ser prácticamente imposible o requerir una inversión muy superior a la que hubiera sido necesaria si se hubiera tenido en cuenta desde el principio.

En el desarrollo de software no es así. Es cierto que el cambio tiene un coste pero no es equiparable al de las construcciones físicas.

Renunciamos a la maleabilidad del software cuando lo importante es el cumplimiento de una agenda, es decir, se prioriza mantener previsiones de costes y plazos sobre la posibilidad de entregar un producto con mayor valor. Hay que analizar cada circunstancia y es posible que no haya otra alternativa que cumplir con la agenda: no es posible dedicar mayor presupuesto al proyecto y/o modificar los plazos, siempre y cuando se entienda que no es posible la cuadratura del círculo: cumplir agendas y tener al producto sometido a continuos cambios de criterio.

Mi apuesta es incrementar el valor del producto que se pone en producción y eso requiere un enfoque iterativo incremental para aprovechar el feedback del usuario. Esto tiene implicaciones presupuestarias que se solucionarían bien teniendo abierto el presupuesto del proyecto o si está cerrado centrarse en que las funcionalidades prioritarias vayan bien (esto último siempre y cuando no nos encontremos en una situación de Death March Project en la cual todo será mucho más complicado).

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.

Cuando el sistema no tiene todavía demasiada entidad y los plazos son relativamente poco importantes se tiende en muchos casos a liberar pronto (buena práctica) pero sin tener excesivo cuidado con la arquitectura y el código.

Es cierto que se asocia esta dinámica al uso de metodologías ágiles pero no tiene nada que ver con el enfoque que se utilice, depende del desarrollador y de su capacidad de analizar las consecuencias de determinadas actuaciones a medio y largo plazo. De hecho con enfoques iterativos incrementales este tipo de problemas saldrán antes a la luz.

Si conforme va creciendo la aplicación se sigue en esa dinámica llegará un momento donde en lugar de tener la deuda técnica bajo control será ella quien te domine a ti.

Ese instante llegará y los costes y velocidad seguirán direcciones opuestas y cada puesta en producción será como una película de suspense por la elevada probabilidad de que se produzcan efectos colaterales.

La competencia es muy dura, si no eres capaz de dar un buen servicio los clientes buscarán otras opciones (imagina que un error en una nueva versión de un servicio que proporcionas a varios clientes les impide trabajar de manera adecuada una jornada entera de trabajo o les haces perder información), si hay un cambio de tendencia en el mercado y llegas tarde, será complicado volver a recuperar tu espacio.

Cuando el usuario quiera cambiar cosas deberá negociar y en esa negociación suelen quedar tocados tanto desarrolladores como usuarios, los primeros porque el esfuerzo a realizar tras el cambio será mayor, ya que el cumplimiento de la agenda recaerá sobre sus espaldas y los segundos porque no podrán llevar al producto todos los cambios que entienden que son necesarios (el valor del producto está fuertemente limitado por el valor teórico del producto en su especificación) y tendrán, probablemente, un producto de mayor deuda técnica (se descuidará la calidad del desarrollo como consecuencia de incrementar el ritmo de trabajo y del más que probable overtime que tengan dedicar) y con una peor ejecución funcional porque se habrá dedicado menos tiempo (si es que se ha dedicado alguno) a la detección y corrección de incidencias y deficiencias funcionales.

Pero, ¿dónde comienza la gestión de agenda?, ¿antes o después de tener el catálogo de requisitos?. La respuesta natural a la pregunta es que poca agenda puedes gestionar si no sabes lo que vas a hacer y en base a ellos se ha definido un presupuesto y unos plazos equilibrados.

Sin embargo, nos encontramos con dos escenarios:

1) Definición de agenda, una vez definido el alcance inicial del proyecto.

2) Definición de agenda, sin tener una versión inicial del catálogo de requisitos y por tanto, con una estimación del presupuesto y plazos un tanto irreal.

El primer escenario es en el que realmente tendría sentido la gestión de la agenda ya que se da por conocido el sistema que hay que desarrollar y para ello se ha dedicado un presupuesto y previsto unos plazos. Sin embargo, al final, el problema sigue siendo el mismo, porque cuándo surjan modificaciones en los requisitos tocará negociar para mantener la agenda y en caso de desacuerdo el perjudicado en primera instancia será el producto final, en segunda instancia todo el resto de implicados.

En el artículo de mañana analizaremos el segundo escenario que, por desgracia, suele ser el más habitual.

En los enfoques clásicos los cambios tardíos en los requisitos suponen un golpe muy duro al proyecto porque suelen llegar cuando se están mostrando los avances en la construcción del proyecto, en las pruebas de aceptación o con el sistema en producción.

Ese feedback dará lugar a nuevas peticiones de cambio por parte del usuario ya advertirán aspectos que no han tenido en cuenta, otros que son mejorables y otros que son incorrectos (ya sea por una mala especificación del usuario, por una mala interpretación de la misma por parte del desarrollador o por una mala ejecución de los trabajos). Como el desarrollo del producto está tan avanzado el coste de estos ajustes será importante en muchos casos, sobre todo si se plantean cambios severos en el núcleo de la aplicación (teniendo en cuenta que la deuda técnica va creciendo conforme crece el tamaño del producto).

En otras palabras, que te cambien los requisitos tarde es un marrón y lo peor de todo es cuando esto sucede en un enfoque predictivo como el clásico en el que teóricamente se espera que esto no suceda (aunque hasta el mayor defensor a ultranza de los enfoques clásicos sepa que esto va a pasar) y en donde resultará complicado hacer ajustes en el presupuesto (algo que habría que hacer cuando los cambios no sean motivados por fallos o negligencia del desarrollador).

¿Es un marrón en un enfoque de carácter evolutivo? No puedo decir que este enfoque sea a prueba de cambios porque no lo es. Si pese a que se ha seguido un desarrollo iterativo incremental en donde el usuario ha aportado su feedback desde etapas tempranas resulta que en fases muy tardías hay cambios severos en el proceso o procesos que se implementan o se descubre que el usuario ha sido negligente te revienta el proyecto, quieras o no, y darle la vuelta al calcetín resulta complicado cuando no imposible si no se ajusta el presupuesto y se resuelven determinados problemas que han dado lugar a esta situación (por ejemplo si el usuario ha sido negligente será necesaria su sustitución).

Partamos de la base de que los cambios son ajustes, unos más grandes que otros pero moderados, y que el objetivo es, como no podría ser de otra manera, desarrollar un producto de calidad que satisfaga las expectativas del usuario, si el usuario detecta que es necesario hacer cambios sobre un producto maduro (incluso con varias iteraciones en producción) se hacen, recordemos que no desarrollamos para nosotros sino que desarrollamos para el usuario y él sabe mejor que nadie qué es lo que necesita y más en estas etapas donde ya están trabajando sobre algo real y en producción.

Lo complicado de esto es que el usuario entienda que todo ajuste tiene un coste, ahí es donde se sustancia el problema, en querer que con un presupuesto inicial realizado desde la más absoluta incertidumbre y con gran desconocimiento del resultado final del producto se cubra todo el trabajo. Si se supera esa resistencia, bienvenidos sean los cambios siempre que los mismos den lugar a una mejor versión de la aplicación.

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 1.714 seguidores