archivo

Archivo de la etiqueta: deuda técnica

Es importante tener en cuenta segundas o terceras opiniones y reflexionar sobre la posible solución a un problema cuando la misma parece compleja a priori y por tanto requeriría un esfuerzo significativo para su realización.

Este ejercicio puede ahorrar días o incluso semanas de programación a lo que habría que añadir que probablemente la solución más simple tenga aparejada una menor deuda técnica.

Es posible que la solución simple implique alguna modificación sobre la dinámica de funcionamiento de la solución que especificó el usuario (es decir, que no sea un aspecto exclusivamente técnico), en estos casos es fundamental exponérsela y probablemente la acepte siempre y cuando la finalidad de la funcionalidad no varíe, hay que tener en cuenta que el usuario cuando realiza la especificación, por más que incluso la haga debatiéndola contigo, no tiene en consideración todas las opciones posibles sino la que en ese momento le viene a la cabeza o la que estuvo pensando la semana anterior.

Si te precipitas probablemente tardarás mucho más que si se analizan las posibles alternativas de solución. Al fin y al cabo será cuestión simplemente de esperar horas o días para madurar qué camino es el más óptimo.

Siempre agradecerás una solución simple, muchas desviaciones en los proyectos se producen precisamente por no dedicar suficiente atención a disminuir la complejidad técnica y funcional.

No importa que la idea no la hayas tenido tu, lo importante es el proyecto y éste es el resultado de la colaboración de todos los que participáis en el mismo. Tenemos el síndrome de la primera piedra, que consiste en que queremos ser nosotros los que coloquemos siempre el primer ladrillo pese a que el siguiente que se ponga sea el quinientos. Es importante dejar el ego a un lado para poder escuchar a tus compañeros.

Como desarrolladores somos reacios a la realización de cambios y no es que seamos así genéticamente sino por el hecho de que se nos ha formado y después hemos trabajado en proyectos en donde nuestra capacidad de ser flexibles estaba muy limitada por el cumplimiento de una agenda de costes y plazos (mucho más lo primero que lo segundo).

Cuando se enfoca un proyecto con presupuestos inmovilistas y sobre todo, carentes de toda aproximación a la realidad no se puede pretender que acojamos los cambios dando aplausos porque lo que el presupuesto no cubre (hasta cierto límite, claro está) lo tendremos que cubrir con nuestro trabajo.

No se trata de trabajar con presupuestos ilimitados, sino de desarrollar con intención (que es lo mismo que tratar de desarrollar de manera eficiente) y de determinar cuándo el valor del producto es suficiente y cuándo deja de ser rentable el crecimiento del valor con respecto a la inversión que se está realizando.

Con esta visión del desarrollo del software el cambio resulta bienvenido ya que se entiende que a través de él se consigue dotar al producto de un mayor valor (como consecuencia de que cada vez se ajustará más al contexto existente y a las expectativas de los usuarios).

Ahora bien, el cambio debe hacerse con intención (evitando en lo posible las circunstancias de prueba y error) y con un sistema en los que los cambios se puedan realizar con agilidad (deuda técnica acorde a las características del sistema que se desarrolla, arquitectura y modelo de datos escalable, etc…).

Cuando uno se acostumbra a este esquema de trabajo se acepta el cambio como una parte más del proceso de desarrollo de software y efectivamente se cumple la siguiente reflexión de Ken Auer y Roy Miller: “La única manera de deshacerte de tu miedo a los cambios en el código es hacerlos una y otra vez”.

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.

La tentación, muchas veces motivada por las circunstancias, es obviar la mejora de la factura técnica del producto, para centrarnos en corregir errores y en incrementar las funcionalidades.

El usuario siente la calidad del software con lo que ve: cumplimiento de sus expectativas de funcionamiento y buen rendimiento y disponibilidad, por eso pondrá resistencia a invertir tiempo en reducir la deuda técnica.

No debemos olvidar que desarrollamos para el usuario y él es quien tiene la última palabra, lo cual no quita que le expongamos la ventaja que tiene la realización de este tipo de tareas, como una inversión que permita no solo incrementar la capacidad de trabajo que puede abordar el equipo, sino además, reducir la posibilidad de efectos colaterales que provoquen errores cuando y donde menos los esperamos.

De serie, un desarrollador debe tener en cuenta estas prácticas y tenerlo en cuenta a la hora de definir su velocidad. El tiempo que se puede dedicar a esto puede ser prácticamente ilimitado porque todo es susceptible de ser mejorado, por ese motivo, ese tiempo de más sobre las buenas prácticas de programación cotidiana es el que se tiene que discutir con el cliente.

Comenta Mary Poppendieck: “La refactorización continua no es opcional. Si se elige no refactorizar se elige no pagar la deuda técnica que con el tiempo demostrará que la posibilidad de fracaso aumenta”.

La clave es mantener la deuda técnica bajo control y esto requiere un esfuerzo desde la programación de base como de las tareas adicionales de refactorización, de acuerdo siempre con las necesidades de quién paga, el cual debe estar siempre informado de las consecuencias que tiene realizar desarrollos sobre un software de baja calidad.

Su enunciado es el siguiente: “Cuando un sistema evoluciona se incrementa su complejidad a menos que se trabaje para mantenerla o reducirla”.

La complejidad crece no solo a nivel de deuda técnica (algo que es razonable por el incremento del tamaño del sistema), sino también a nivel de administración, usabilidad y recursos software y hardware necesarios para su funcionamiento.

Si se tiene cuidado y se aplican buenas prácticas, este incremento de la complejidad puede hacerse más moderado e incluso puede reducirse si se aplican medidas específicamente destinadas a este fin (que se complican cuando a la vez se están incorporando nuevas funcionalidades al sistema).

Lo que viene a decirnos (de la mano de la Primera Ley) es que con el paso del tiempo va a ser necesario evolucionar las aplicaciones y que ese coste será progresivamente mayor conforme se realicen nuevas actividades de mantenimiento en el sistema.

Para Donald Norman: “Los objetos bien diseñados son fáciles de interpretar y de comprender ya que contienen pistas visibles de su funcionamiento”.

Un código fácil de leer y de entender en entornos altamente colaborativos como son los equipos de trabajo ágiles resulta esencial. También lo es para otros tipos de enfoques.

No es sencillo, se requiere tiempo, conocimientos y experiencia, también nos podemos apoyar con herramientas que nos ayuden a aplicar buenas prácticas y también se requiere ser sistemático y creer en los beneficios que aporta hacer el trabajo de esta manera, de lo contrario se te pondrá muy en cuesta arriba refactorizar, tanto tanto, que no lo harás.

Hacer esto bien requiere dedicarle el tiempo que necesita, cuando estamos en proyectos ligados a plazos y los recursos disponibles son muy limitados, se convierte en objetivo ejecutar todo el trabajo posible y se deja de lado la calidad del código ya que en esos momentos conceptos como la deuda técnica no se encuentran entre las prioridades del desarrollador. Es cierto que es contraproducente ya que una deuda técnica elevada hace más pesado el proceso de desarrollo pero cuando el fuego te está llegando a los pies solo piensas en correr.

Por ese motivo creo mucho más en el valor del software que en las agendas, un buen software requiere del tiempo y de las personas necesarias para hacerlo, sin equilibrio la calidad del software sufre (también la cuenta de resultados del proyecto).

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”.

La siguiente cita de Bertrand Meyer puede crear algo de controversia (traducción libre): “Que esté correcto es claramente la primera calidad. Si un sistema no hace lo que se supone que debe hacer, entonces todo lo demás importa poco”.

¿Por qué digo que es controvertida? Si se entiende a partir de ella que el fin último es satisfacer las necesidades de los usuarios a través de un producto que se aproxime a lo que estaban esperando y que para eso todo vale, puede considerarse como polémica porque la calidad del software va más allá de eso, es más, probablemente sin esos otros ingredientes resulta mucho más complicado conseguir ese objetivo.

De hecho, si en el software se va acumulando tanta deuda técnica que hace prácticamente inmanejables las tareas de evolución del producto, tenemos un problema importante porque si bien se ha podido conseguir un producto del que estén satisfecho los usuarios, es posible que esa satisfacción sea efímera porque lo más probable es que el producto tenga que modificarse en un futuro, sea a corto, medio o largo plazo y si como consecuencia de las dificultades para adaptar el sistema al cambio no es posible en forma y/o en tiempo (y/o en la cantidad de dinero que es posible invertir), el éxito inicial se habrá convertido en un espejismo.

Como es lógico, Bertrand Meyer pone sobre la mesa algo muy importante y que los desarrolladores a veces perdemos de vista y es que, por regla general, desarrollamos un software para que lo utilice un tercero y no para:

– Seguir procesos por el simple hecho de seguirlos, sin tener en cuenta la realidad y contexto del proyecto y producto en el que estamos trabajando.

– Seguir determinadas técnicas que pudiendo ser beneficiosas no resultan prácticas para este proyecto concreto.

– Ponernos a hacer experimentos que si no tienen como fin obtener un aprendizaje que permita dar un mayor valor al producto directa o indirectamente, se deberían evitar.

Este es uno de los principales problemas del desarrollo de software y da igual la metodología o estrategia que estés aplicando.

Los equipos y desarrolladores que saben que la calidad se obtiene en el día a día de tu trabajo y no mediante controles en momentos puntuales del proyecto, funcionan mejor y obtienen, en consecuencia, mejores resultados.

El problema es que se cree que de esta forma se avanza más deprisa pero no deja de ser una ilusión porque si te echan para atrás una entrega en reiteradas ocasiones todo lo que se adelantó se termina convirtiendo en retrasos y no se trata solo de plazos, sino de esfuerzo que se tiene que volver a invertir (más costes).

Tampoco se trata de problemas de índole funcional o de detección de defectos, se trata también de la propia calidad de la arquitectura y del código, de la deuda técnica. La refactorización consigue disminuir esta problemática y es una buena práctica pero lo ideal es que venga acompañada con intención en el desarrollo, es decir, de intentar que el código y las decisiones relacionadas con la arquitectura sea lo mejor posible de serie.

Hablo de mejor posible, no de perfección, hay un camino muy largo entre ambos conceptos, teniendo en cuenta que la perfección en el desarrollo de software no es más que un concepto abstracto y retórico.

¿Cuándo queda para que el proyecto termine?.

Interesante visión la que plantea la Ley de Hartree: “El tiempo desde ahora hasta la finalización del proyecto tiende a ser constante”.

Sería más exacto si en lugar de hablar de proyecto hablásemos de ciclo de vida del producto software, ya que se entiende al proyecto como un conjunto de actividades que se realizan en el tiempo para conseguir unos resultados (mucho más cortoplacista y concreta de lo que sería el ciclo de vida).

Si bien, nos encontramos pese a todo, con proyectos que prácticamente no parecen tener fin, ya sea porque los objetivos (para los cuales se realizan esas actividades) son demasiado abiertos o ambiguos y/o porque la ejecución de las trabajos por parte del equipo de desarrollo es deficiente, teniéndose que volver a realizar de nuevo o parcialmente determinadas funcionalidades, por una deuda técnica elevada, lo que hace que la mantenibilidad o evolución del sistema requiera más tiempo y esfuerzo, etc…

Ya sea tomando como referencia los proyectos o el ciclo de vida, lo que sí resulta interesante de esta ley son los mensajes que podemos interpretar a partir de ella:

– Los proyectos de desarrollo de software suelen tener objetivos demasiado abiertos o ambiguos.
– Las estimaciones son menos precisas cuanto más cerca nos encontremos del inicio del proyecto.
– La deuda técnica de manera paulatina hace más complejo el proceso de desarrollo de software.
– Un producto software de manera inherente tiende a ser evolucionado (ver leyes de Lehman).