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.

La definición de smoke testing es sencilla ya que son pruebas rápidas que se realizan sobre aspectos funcionales del software para comprobar a alto nivel si el mismo tiene el comportamiento esperado. Ahora bien, la forma y el momento de aplicación puede variar.

Lo más normal es que se aplique durante el proceso de construcción como un paso más allá a la integración continua y a las pruebas unitarias y pueden ser automáticas, manuales o mixtas, ya que lo que se pretende comprobar es si determinadas funcionalidades tienen el comportamiento adecuado.

Hay que tener en cuenta que el smoke testing más que descubrir errores concretos lo que pretende es verificar que a nivel funcional no se están produciendo incidencias graves.

En otros contextos se aplica smoke testing con caracter previo a pruebas funcionales más exhaustivas con el objeto verificar si merece la pena ponerse con ellas, es decir, si se detectan errores graves (el sistema no funciona, sea cae a la más mínima operación, no abre una pantalla que permite acceder a una funcionalidad esencial, etc…), ¿para qué perder el tiempo haciendo un testing en mayor profundidad del sistema?

También es frecuente aplicar este tipo de testing con carácter previo a una entrega al cliente, de manera que se realice una última comprobación para verificar que a alto nivel todo está bien (antes como es lógico se debería haber realizado unas pruebas en profundidad del sistema de información a todos los niveles, no solo el funcional).

Uno de los aspectos más complicados del testing consiste en definir hasta dónde se va a llegar, es decir, qué tipo de pruebas realizar y con qué intensidad, ya que todo el software no tiene la misma criticidad, los mismos recursos para su desarrollo, los mismos plazos, etc…

Es un error aplicar el mismo nivel de testing a todos los productos, ya que podemos pecar por exceso o por defecto (siempre mejor, como es lógico el exceso) y además hay que tener en cuenta que el testing se debería medir más por su efectividad y por su calidad que por el tiempo dedicado al mismo, si bien, se entiende que un equipo formado, aplicando una serie de metodologías y utilizando una serie de herramientas, mantendrá un nivel de efectividad más o menos regular por unidad de tiempo.

A lo anterior hay que añadir que el testing se debe realizar en todo el proceso de desarrollo y no solo al final y que el mismo equipo de proyecto debe participar en el mismo mediante la aplicación de pruebas unitarias, integración continua, pruebas funcionales, verificaciones continuas con el usuario y obtención e implementación del feedback, etc…, independientemente de que existan una serie de profesionales que sean especialistas en realizar este tipo de trabajo.

El testing es muy importante y no una disciplina secundaria, no es algo que se debiera infravalorar, como en el mundo de la programación hay programadores buenos, malos y regulares, lo mismo pasa con los testers. Un producto que llega a producción con errores graves va a costar dinero, de una u otra forma. En algunos casos estos errores serán críticos y el coste será elevadísimo en otros casos provocará un parón en el servicio que no es poco.

Resulta muy interesante la siguiente cita de Weinberg porque refleja bien a las claras lo crítico que resulta en muchas ocasiones el proceso de testing y lo complicado que resulta establecer sus límites (traducción libre): “En septiembre de 1962, saltó a la luz una noticia que indicaba que un cohete de 18 millones de dolares había sido destruido en pleno vuelo debido a un simple guión que faltaba en un programa. La naturaleza de la programación es así, ya que no existe relación entre el tamaño del error y los problemas que causa. Por tanto, es difícil definir cualquier objetivo en el proceso de testing, sin llegar a la eliminación de todos los errores, algo que resulta imposible”.

¿Cuánto tiempo dedica tu equipo de proyecto a realizar testing?, ¿cuál es la proporción respecto al esfuerzo dedicado netamente a desarrollar?, ¿se utilizan estrategias que automaticen determinados tipos de test como por ejemplo las pruebas unitarias?, ¿vas más allá y aplicas TDD?, ¿usas integración continua?, ¿en qué etapas realizas pruebas?, ¿comienzan desde el mismo proceso de análisis?, ¿cuál es la participación del usuario (o el cliente en general) en las mismas?, ¿en qué proporción es explotaratoria y en qué proporción se basa estrictamente en casos de prueba?, ¿participan personas distintas al equipo de proyecto en pruebas de tipo funcional?, ¿se realiza testing de seguridad, usabilidad, rendimiento?, dicho testing, ¿cuánto es manual y cuánto está asistido por herramientas?, ¿no se realizan pruebas de la aplicación en un entorno de integración del cliente?, ¿realizas análisis estático de código?, en dicho análisis, ¿tienes definidos unos umbrales mínimos de calidad exigibles a las distintas métricas?, cuando vas a entregar una nueva versión de un producto software, ¿haces pruebas de regresión?.

Aunque tal vez todo lo anterior se pueda resumir en par de preguntas, ¿qué nivel de importancia le das al testing en las aplicaciones que desarrollas?, ¿tienes mecanismos para comprobar si se realiza testing y para evaluar la efectividad del mismo?.

El testing se infravalora de manera muy injusta. Bien realizado ahorra muchos problemas y por encima de eso, permite conservar tu imagen y tu dinero.

Hay muchos que piensan que el testing no es ágil y yo respeto las opiniones de cada uno, independientemente de que las comparta o no. Como he dicho muchas veces, lo que no es ágil es tener que repetir trabajo no por evolucionar una funcionalidad o un componente software, sino para corregir errores que perfectamente detectables en etapas anteriores.

Es más ágil (y menos arriesgado) prevenir un incendio que intentar sofocarlo después.