archivo

Archivo de la etiqueta: integración continua

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.

No hay que despreciar el importante feedback que nos proporciona la aplicación de técnicas de integración continua y de automatización del testing en un proyecto de desarrollo de software.

Es un feedback diferente al que proporcionan los usuarios (centrado fundamentalmente en aspectos funcionales y de experiencia de usuario) pero no por ello se debe considerar irrelevante ya que informa precisamente de si el software se está alejando o no de ese comportamiento funcional que se espera se tenga y esa desviación se puede producir en diferentes niveles, desde el unitario, pasando por la integración y terminando en el sistema (da mucha confianza saber que lo que vamos construyendo es coherente y que no está estropeando nada de lo ya implementado).

La integración continua y la automatización del testing requieren un esfuerzo (que hay que medir bien en función de la naturaleza del sistema que se está desarrollando) que obtiene su retorno de la inversión en la detección rápida de errores y en la disminución o erradicación (en función del nivel de alcance del testing) de efectos colaterales y que no requiere, una vez implementado, ningún tipo de intervención humana, más allá de los mantenimientos que sean necesarios realizar o de la corrección de las incidencias y problemas que sean detectados a través de los mismos.

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.

Cuando se desarrolla una serie de módulos de una aplicación por parte de un equipo de trabajo dentro de un equipo de proyecto y/o la producción se encuentra delegada en diferentes equipos existe un cierto temor por cada uno de ellos por integrar su trabajo dentro de la línea principal del producto (a incluso más bajo nivel los propios programadores tiene un cierto respeto a consolidar sus desarrollos dentro de la línea de trabajo en la que colabora).

Si este temor se traduce en retrasar todo lo posible el proceso de integración, no solo no conseguiremos evitar lo inevitable (que es tener que integrar) sino que además el número de problemas que nos encontraremos será, por regla general, mucho mayor que si hubiéramos realizado una integración continua o, al menos, con una periodicidad razonable. Algo que lo mismo obliga a tirar buena parte del trabajo realizado.

Se han realizado estudios que indican que más del 50% de la corrección de errores introducen errores adicionales en el código.

Por este motivo resulta fundamental la aplicación de buenas prácticas en la codificación, la utilización de una buena arquitectura y framework, la aplicación de pruebas unitarias y de integración continua y la realización de nuevas pruebas una vez que se haya integrado un componente corregido.

Por esto es tan importante que el proceso de desarrollo no sea algo improvisado sino algo reglado, donde cada miembro del equipo de proyecto conozca qué es lo que tiene que hacer y qué técnicas tiene que utilizar para programar.

Barry Boehm es una buena referencia a la hora de considerar como adecuados determinados indicadores obtenidos en sus investigaciones y proyectos, no en vano siempre ha sido considerado como una de las referencias en el campo de la ingeniería del software, metodologías de desarrollo, estimación de costes y obtención y determinación de métricas relacionadas con el proceso de desarrollo.

Independientemente del indicador que cita Boehm y que indicaré al final de artículo, la propia experiencia nos dice que el coste de corrección de un error se dispara conforme nos alejamos del momento en que se ha producido. Esto es así porque un error arrastra tras de sí otros trabajos que correctos o no, se verán alterados por la corrección del mismo. Por ejemplo, una mala especificación de un requisito que llega a producción tendrá repercusión en el producto final, pero también su corrección requerirá un esfuerzo considerable por los diferentes artefactos que se tienen que modificar o incluso rehacer.

La aparición de los modelos de testing en V o de testing en W son una respuesta posible ante esos problemas, como también lo son la aplicación de otras técnicas en el proceso de construcción, como por ejemplo pruebas unitarias, integración continua, etc…

No solo afectó esta circunstancia a la aparición de modelos o técnicas de testing, sino que las metodologías de una u otra manera quedan impregnadas por la necesidad de detectar y corregir cuanto antes los errores (de hecho el propio testing en V no es más que una variante del ciclo de vida clásico o en cascada) como por ejemplo podemos ver en RUP o por la propia dinámica de desarrollo siguiendo metodologías ágiles.

Sobre la detección tardía de errores y sus implicaciones en los costes de un proyecto, Barry Boehm comenta lo siguiente (en relación a errores en etapas muy tempranas en el desarrollo, como lo son la obtención de especificaciones o requisitos): “Corrige los errores de especificación cuanto antes. Corregirlos después, costará: un 500% más en la etapa de diseño, un 1000% en la etapa de codificación, un 2000% en la etapa de testing unitario y un 20000% más en la entrega”.

El funcionamiento de un producto software se define mediante la integración de sus componentes internos y la integración con componentes externos.

En un ambiente de arquitectura del software donde la modularidad y la delegación de funcionalidades predomina sobre otros paradigmas y en donde los equipos de trabajo pueden estar en diferentes localizaciones geográficas, un gobierno adecuado de integración de componentes resulta fundamental para detectar cualquier anomalía lo antes posible.

CMMI no es ajeno a esta circunstancia y determina en su nivel 3 la necesidad de establecer una estrategia global. Perfectamente podría haber incluido a nivel de proyecto este proceso en el nivel 2, sin embargo la orientación del mismo a la administración o gestión de cómo se realiza al desarrollo y no a la definición de métodos de trabajo, es lo que ha hecho que este proceso al igual que otros pase a engrosar este nivel.

Por tanto, en este proceso se establece una metodología a nivel de organización en cuanto a la integración de componentes. Como en el resto de procesos, la definición de una estrategia global no consiste en la definición de un modelo único sino que permite escenarios en función de la tipología y características del proyecto.

En la descripción de este proceso se encuentra por un lado los pasos a seguir para preparar la integración (determinar la secuencia de integración, adecuar un entorno de integración y decidir cómo y a través de qué se realiza el proceso de integración), para garantizar la compatibilidad de las interfaces (mejor si se detectan problemas de este tipo antes de realizar la integración) y para realizar el propio proceso de integración (condiciones que tienen que cumplir los componentes con carácter previo a la integración, cómo realizar la misma y cómo evaluar el resultado final).

La implantación de este área de proceso no debe resultar complicada para la mayoría de las empresas de desarrollo de software ya que con carácter previo han tenido que gestionar de manera adecuada la gestión de la configuración (esencial para restar complejidad a este proceso) y porque generalmente cuentan con soluciones de integración de componentes y en muchos casos se aplican técnicas de integración continua y en menos, pero afortunadamente cada vez más, pruebas unitarias y técnicas rápidas de verificación estructural y funcional como smoke testing.