archivo

Archivo de la etiqueta: programación

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.

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

Supuestamente dentro de un proyecto de desarrollo de software todo el equipo tiene acceso al código que se está desarrollando y, por tanto, se podría considerar que existe una propiedad colectiva del mismo, sin embargo esa apreciación es más teórica que real, porque lo que suele suceder es que los desarrolladores pongan reparos en que otros toquen lo que han construido. Somos así, ¿qué le vamos a hacer?.

Considero un acierto por parte de XP que considere como práctica la propiedad colectiva del código, es decir, que se fomente que cualquiera pueda trabajar y mejorar con cualquier sección del código la hubieran desarrollado ellos o no. Y no lo comento solo por motivo de dar una mayor flexibilidad o disponibilidad al equipo, sino porque otro punto de vista generalmente suele ser positivo (por eso ese mismo enfoque de desarrollo tiene entre sus recomendaciones la programación por pares).

Para que este concepto se aplique de manera efectiva en un proyecto es fundamental el respeto. En el momento en que se pierden las formas por la solución en que una persona ha realizado un desarrollo, se empiezan a levantar muros. Y es que es fácil reirse o llevarse las manos a la cabeza por determinadas codificaciones pero realmente haríamos lo mismo si revisásemos código nuestro construido hace años (o tal vez no desde hace tanto tiempo).

También es importante no olvidar que se tiene que desarrollar con intención con propiedad colectiva o sin propiedad colectiva, es decir, no se trata de ponerme a retocar tal o cual funcionalidad o a refactorizar tal o cual clase o módulo, sin un propósito o poniendo en riesgo los compromisos que el equipo ha pactado para el sprint.

Principio 5: Ya que las pruebas solucionan solo una fracción de los defectos, debes tener pruebas de calidad.

El día que los desarrolladores asimilemos esto, las cosas empezarán a cambiar.

Tenemos un mal endémico, no probamos. Esto es así y cuando lo hacemos nuestros resultados no suelen ser buenos ya que no le prestamos a esta actividad la importancia que merece.

Para muchos desarrolladores, lo que importa es parar el reloj y dar por completada la tarea. Esto es como si un jugador de ajedrez realizara un movimiento sin haber estudiado bien la situación, es probable que la partida la lleve bien de tiempo, pero probablemente la tenga muy mal en el tablero.

Después, cuando empiezan a aparecer errores, vienen las prisas por corregirlos, las cuales traerán nuevos errores, además de los que no se han terminado de arreglar, entrando en un ciclo que hace mucho daño al proyecto y al producto, porque no solo afecta a la velocidad de desarrollo o la confianza de los usuarios en nuestro trabajo, sino que económicamente puede tener efectos devastadores para el que desarrolla, al tener que rehacer una y otra vez diversas porciones del código y para el cliente, con un sistema en producción que no puede ser utilizado de manera adecuada y/o que no se adapta a las necesidades y expectativas puestas en él.

El testing se puede hacer de muchas maneras y a diferentes escalas, intercalando procesos automáticos y manuales, interviniendo desarrolladores, usuarios y personal independiente, de manera continua durante el desarrollo y como última barrera de defensa antes de la entrega.

Y no solo se trata de testing, es necesario gestionar las expectivas de manera continua.

El esfuerzo que sería necesario dedicar al testing es potencialmente ilimitado. En ese momento entra en juego las características del sistema que estamos desarrollando y el presupuesto disponible. Se trata de ser eficiente, productivo, de aprovechar de la mejor manera posible nuestro esfuerzo. Probar por probar no sirve de nada, se tiene que hacer con intención y sabiendo muy bien lo que se hace.

El desarrollo de software es una disciplina complicada y que generalmente es poco generosa con las personas que día a día trabajan en ella.

Construir un producto, ladrillo a ladrillo, línea a línea (por mucho que nos podamos apoyar en frameworks, generadores de código y librerías) es una actividad compleja, sobre todo teniendo en cuenta que el desarrollo de software no es solo programación. Para ejecutar (programar) es necesario haber llegado antes a la determinación y conocimiento de qué es lo que se va a hacer y eso resulta complejo y puede ocasiones desgaste entre las partes sobre todo si los contextos no son favorables y/o las personas no ponen de su parte.

A los desarrolladores nos cuesta mucho trabajo desconectar cuando salimos del trabajo sobre todo cuando hay problemas y desgraciadamente nos encontramos con problemas con demasiada frecuencia.

Y dentro de trabajo si nos encontramos motivados o si el propio reto o problema nos motiva toda nuestra atención se dirigirá a la resolución del mismo independientemente de que la misma nos lleve días (o semanas). Cuando entramos en lo que los expertos en productividad llaman «la zona» parece que no existe el tiempo y de existir siempre nos va a aparecer que avanza demasiado deprisa (no es sencillo gestionar esto cuando tenemos otras tareas que también están pendientes de nuestra atención).

Creo que es interesante reflexionar sobre la siguiente cita de Orson Scott Card (traducción libre): «La programación (el desarrollo de software) es el gran juego. Te consume a ti, tu cuerpo y tu alma. Cuando estás atrapado en él, nada importa».

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.

Puedes trabajar con factorías de software siguiendo el modelo que más pueda convenir: Offshore, Nearshore, Onshore y conforme exista más distancia entre los equipos que tratan las especificaciones y el equipo que las desarrolla más mecánico se convierte el trabajo del equipo de programadores: «toma las especificaciones, te las modelo y desarrolla según la arquitectura y framework pactado».

Es posible que esa distancia la puedan reducir los equipos de trabajo aplicando distintos tipos de técnicas y herramientas porque como he dicho en otras ocasiones el impacto de la distancia depende mucho de la actitud de todas las partes. Sin embargo cuando por encima de los técnicos, hay jefes de proyecto o gerentes «contables» la actitud (si existe) queda difuminada y se entra en un peligroso modelo de trabajo basado en el antipatrón «arrojar al otro lado del muro».

Como bien dice un amigo, no hacen falta factorías de software para caer en ese antipatrón, te puede pasar perfectamente con el proveedor incluso compartiendo oficina.

En el momento en que se entra en ese juego la programación desgraciadamente se convierte en una actividad mecánica: el programador se limita a ejecutar tareas, conociendo solo el sistema que se desarrolla a bajo nivel. Con esto perdemos el feedback del programador tanto a nivel técnico: tal vez sean recomendables ciertos cambios en la arquitectura o el framework para adaptarlos mejor a la naturaleza del software que se desarrolla, como funcional: Esto no termina de ser coherente con otros módulos del sistema y es necesario que las especificiones sean más claras o que se estudie esa falta de consistencia con el usuario.

En un proyecto todos suman, los programadores por supuesto también (y mucho). Todas las personas que están en el proyecto están capacitadas para aportar ideas y soluciones, tengan el rol que tengan, a veces estarán acertadas y otras se equivocarán pero lo que no podemos hacer es dejar de escuchar sus opiniones porque estaremos despreciando toda la capacidad y talento de los componentes de nuestro equipo.

Interesante la reflexión que Martin Fowler realiza en su libro «Refactoring: Improving the Design of Existing Code» (traducción libre): «Al compilador no le preocupa que el código sea feo o limpio. Pero cuando cambiamos el sistema, hay una persona involucrada y ahí sí que importa. Un sistema mal diseñado es complicado de modificar».

En última instancia somos las personas las que tenemos responsabilidad sobre la deuda técnica no hay nadie más entre nosotros y el código. Es cierto que podemos apoyarnos en generadores de código y en frameworks pero es decisión de la organización, del departamento o nuestra elegirlos y en muchos casos de su evolución. Son importantes en cuanto a que te evitan la realización de tareas mecánicas y en cuanto a que marcan un camino común en el desarrollo pero hay que tener en cuenta que es importante que el código generado sea de calidad porque es posible que en un futuro el equipo que se vaya a encargar de seguir evolucionando el producto no disponga de ese generador (desgraciadamente es algo que casi nunca se tiene en cuenta).

Hay software como Sonar que utilizado de manera conjunto con un software de integración continua nos permite conocer al día el estado de la deuda técnica pero se trata solo de métricas que nos pueden alertar de situaciones anómalas (o potencialmente anómalas) al final habrá una persona (no necesariamente un gestor ya que el programador también tiene mucho que ver en esto) que tomará la decisión de refactorizar o no determinadas partes de la aplicación.

Lo más recomendable es que la propia refactorización sea parte del proceso de desarrollo que surja como algo natural y que nos apoyemos en software para conocer si hay módulos en donde el trabajo de refactorizar no ha sido suficiente o no ha sido bueno.

Este antipatrón surge como consecuencia de múltiples cambios en una aplicación que se llevan a cabo de forma simultánea en el mismo, de manera que es posible que se haya aplicado la misma solución en diferentes partes del código, de manera que tendremos clases y/o métodos con un comportamiento parecido aunque tengan codificación diferente.

También es posible que se haya llegado a él, no por el hecho de no poder controlar la programación de personas distintas en diversas funcionalidades de la aplicación, sino por la mala práctica de ir copiando secciones de código, realizando las modificaciones oportunas, achacándolo a las prisas y a la presión por la entrega y/o por terceros.

Como consecuencia tenemos un código más complejo, con más deuda técnica y, por tanto, con un mayor esfuerzo de evolución y mantenimiento (hay que tener en cuenta que es posible que con estas prácticas, para hacer un cambio sea necesario hacer modificaciones en tantos puntos como ese código haya sido replicado).

El desarrollo de software es un continuo adelante y detrás no solo fruto del feedback o del cambios en las especificaciones sino también como parte del proceso de construcción. ¿Quién no tiene que corregir errores?, ¿quién no mejora una sección del código que no termina de convencerle?, ¿quién no tiene que realizar ajustes como consecuencia de evolución solicitada por el área usuaria?.

Este tema lo traté en artículo: «¿Qué es el mantenimiento?» y lo vuelvo a hacer ahora porque realmente el hecho de que en el desarrollo de software la frontera entre la construcción y el mantenimiento sea tan débil (por no decir inexistente) me parece muy interesante ya que refleja un aspecto inherente al desarrollo de software como es su naturaleza evolutiva, es decir, el sistema se desarrolla poco a poco y con el paso del tiempo va adquiriendo forma, en todo ese proceso hay evolución y no solo por los aspectos funcionales (o no funcionales) que se van añadiendo, modificando o eliminando sino porque en ese proceso el software también cambia y no necesariamente para cambiar una funcionalidad (refactorización, corrección de incidencias, etc…).

Esta naturaleza evolutiva junto a otra característica inherente al proceso de desarrollo como es la incertidumbre ponen de manifiesto que el software estará sometido a cambios a lo largo del proyecto (por muy diversas causas) y que será necesario habilitar los mecanismos necesarios para que la adaptación a los mismos se realice lo antes posible.

Me gusta el enfoque que Dave Thomas da sobre este tema (traducción libre): «Si nos fijamos en el tiempo que pasamos programando, escribimos un poco y luego volvemos atrás y hacemos un cambio. O volvemos atrás y corregimos una incidencia. O lo tiras todo o lo reemplazas por otra cosa».