archivo

Archivo de la etiqueta: codificación

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.

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

En el desarrollo de software los extremos no son buenos. A veces se le da al programador un diseño tan detallado que básicamente lo que hace es traducirlo a código fuente en el contexto del framework con el que trabaja. Otras veces no existe diseño o bien las especificaciones no son completas y son los programadores solos o en colaboración con analistas los que rellenan los huecos.

Desde mi punto de vista es importante que el programador sepa lo que tiene que hacer, es decir, que las especificaciones las conozca y que también conozca los objetivos no funcionales pero sobre esa base es necesario darle libertad para que dentro de esas restricciones pueda realizar la solución que resulta más conveniente.

Es necesario tener en cuenta que si se realiza un desarrollo en base a un análisis y/o diseño que se realizó hace tiempo es muy posible que quien lo hiciera no tuviera en cuenta determinados factores que sí son conocidos en el presente y no resulta lógico no echar cuenta a una solución mejor por tener en cuenta un trabajo que se efectuó sin conocer determinados tipos de detalles.

También es importante precisar que ese margen de maniobra permite que el programador dentro de esas restricciones pueda expresar su creatividad (algo que todos llevamos dentro) y realice su trabajo más motivado, algo que todos sabemos termina produciendo resultados de mayor calidad.

Como siempre es importante evaluar el contexto. No hay verdades absolutas. Es posible que en determinadas situaciones tener un nivel de detalle importante resulta fundamental, por ejemplo, cuando se externaliza a otro equipo de trabajo las tareas propias de programación de un desarrollo (o parte de él), aunque incluso en estos casos hay que tener en cuenta que la comunicación seguirá siendo necesaria (y se debe considerar un problema si no la hay) porque siempre quedará algún detalle que no se ha especificado al 100%.

La deuda técnica condiciona el coste que tienen las tareas de evolución o mantenimiento de un software. Desde ese punto de vista todo software que sea susceptible de ser evolucionado debe tratar de mantener una deuda técnica acorde a las características del proyecto que se desarrolla y de su contexto (si los recursos para un proyecto son insuficientes la deuda técnica se verá impactada).

Ahora bien, si se trata de un desarrollo de usar y tirar, por regla general aquellas utilidades de pequeño tamaño que se hacen de manera específica para cumplir un objetivo concreto. Por ejemplo, hacer una migración o una explotación de datos no requieren un control de la deuda técnica si bien, siempre es recomendable intentar hacerlo de la mejor manera posible y si tenemos buenas prácticas de programacion, ¿por qué no aplicarlas?. ¿Qué no tendría sentido? Dedicar más tiempo del necesario para realizar ese desarrollo y por supuesto dedicar tiempo a refactorizar la solución.

Es importante programar al mayor nivel posible que nuestro conocimiento y experiencia nos permita. Esa debe ser la base a partir de ahí solo queda seguir aprendiendo y seguir adquiriendo experiencia para a partir de ese punto desarrollar software de mejor factura técnica.

Ahora bien, ni el conocimiento ni la experiencia son las que pulsan las teclas, somos nosotros y si no sumamos actitud a nuestra aptitud, el software que se desarrolla estará por debajo de nuestras posibilidades y por tanto estamos añadiendo deuda técnica extra que podía ser perfectamente evitable.

Es cierto que a veces no es tanto cuestión de aptitud sino el contexto en que el que trabajas donde lo mismo la presión y las prisas provocan que se priorice la ejecución de las tareas a cómo realmente se están ejecutando. Estas circunstancias condicionan el trabajo y provocan una bajada en nuestro listón potencial de calidad, pese a eso hay que intentar ajustar el software a ese nuevo listón y considerar todo lo que esté por debajo de él deuda técnica extra que añadimos al producto.

Después si la metodología de desarrollo, nuestra visión sobre lo que debe ser el producto software y las prioridades del proyecto lo permite habrá un tiempo para refactorizar pero como siempre digo esa actividad se debe realizar siempre sobre un software sobre el que se haya puesto el mayor empeño posible en hacerlo bien.

Al final toda la deuda técnica añadida al software son intereses sobre el desarrollo (y el mantenimiento) que tenemos que pagar, por lo que si hoy sale barato (desarrollar software sin tener en cuenta su mantenibilidad) que por cierto está por ver si realmente sale barato, mañana será caro, muy caro.

Me gusta mucho la siguiente cita de Bob Martin al respecto (traducción libre): “Hacer código de calidad no lleva tiempo. No hacer código de calidad sí que lleva tiempo”.

La visión de la refactorización no debe ser la de: “trato de cerrar cuanto antes las tareas que tengo asignadas que ya tendré oportunidad después de dedicar tiempo a poner el código un poco mejor”, principalmente por dos razones:

1) La mayoría de las veces, salvo que esté muy interiorizado por el equipo de trabajo y por el programador la necesidad de refactorizar y los beneficios que trae consigo, no se termina dedicando tiempo a realizar estas tareas porque continuamente van a surgir otras que se considerarán más prioritarias y sobre las que recaerá la atención. En este contexto tomar ese tipo de actitudes no es más que querer engañarnos a nosotros mismos: “finalmente no pude poner el código mejor o reducir el acoplamiento entre esas clases porque estoy hasta arriba de trabajo y no tengo tiempo”.

2) Se descuida la programación dejando para más adelante detalles que perfectamente podrían ser resueltos conforme se está trabajando. Todo es susceptible de mejorar de ahí lo útil que resulta refactorizar pero es importante que los esfuerzos dedicados a esa tarea sean los necesarios (y no más).

Es cierto que a veces las restricciones temporales imponen unas condicionen que ni permiten refactorizar y ni siquiera permite cuidar lo que se quisiera la codificación y la arquitectura. En estos casos el contexto del proyecto manda y probablemente para conseguir el resultado final en el tiempo y presupuesto establecidos habrá que sacrificar parte de su mantenibilidad y robustez.

¿Qué se puede hacer en estos casos? Lo que se pueda (realmente la situación de partida del proyecto es la culpable de todo lo demás) pero siempre con la intención de hacerlo de la mejor forma posible.

Comenta Bjarne Stroustrup que: “Cualquier solución detallada y tediosa es propensa a errores porque los programadores se aburren”.

Esto sería extensible a cualquier tipo de tarea que un programador no considere interesante o que sienta que no le corresponde.

Si el programador pierde la atención en el problema es evidente que tenga como consecuencia la existencia de una mayor tasa de errores en el software que desarrolla.

¿Soluciones? Pasan por mantener la atención y la motivación del desarrollador. A continuación indico algunas buenas prácticas a tener en cuenta:

– Hay que hacerle ver a los desarrolladores (en general, no solo a los programadores) que no siempre vamos a poder estar innovando y trabajando en proyectos interesantes. Trabajamos en una organización que para que nos siga pagando un sueldo tiene que tener ingresos y los mismos proceden de proyectos de muy diversa índole. Si queremos otra cosa tal vez nos hemos equivocado de lugar de trabajo y se debería pensar o en emprender un negocio o cambiar a otra empresa que te permita realizar el tipo de tareas que te gustan.

– Proyectos largos y con entregas muy espaciadas, reducen la tensión en el equipo. Las entregas en ciclos cortos ayudan a mantener esa tensión. Es conveniente intercalar ciclos donde la presión sea menor, ya que el cansancio también incrementa la tasa de errores (un proyecto de desarrollo de software es una carrera de larga distancia y como tal hay que gestionar adecuadamente los esfuerzos).

– Salvo que sea absolutamente necesario y solo en circunstancias excepcionales y por tiempo reducido no se debe incrementar la jornada laboral de los desarrolladores. Si el equipo de proyecto se sacrifica por la organización, la organización también debe sacrificarse por el equipo.

– Si el equipo está cohesionado y tienen un objetivo común favorecerá la autogestión y como consecuencia el mismo equipo mantendrá la tensión de sus integrantes.

– Que un trabajo bien hecho sea recompensado, adaptando la recompensa a los resultados reales, al esfuerzo realizado y a la complejidad del reto. El trabajo mal hecho también se debe tener en cuenta. Una gestión lineal en la que nunca pasa nada es perjudicial para la productividad.

– Integración continua y testing automático, proporcionan consistencia.

¿Comentar o no comentar?, ¿cuándo o qué comentar?. Soy de la opinión de que los comentarios en el código deben ser los menores posibles y que cuando se utilicen sea por una causa que lo justifique y que realmente aporte valor a quién lo lea.

Cuando se comenta hay que cargar con los comentarios en todo el proceso de desarrollo del software y en las fases posteriores de mantenimiento de lo contrario se darán situaciones de falta de coherencia entre comentarios y código.

Ya lo dice Ron Jeffies: “El código nunca miente, los comentarios a veces”.