Patrones en Gestión de Configuraciones Software (V)

Regression Test

En esta ocasión, el problema a plantear es cómo conocer si el código en un momento determinado durante el desarrollo no habrá empeorado desde los últimos cambios mientras se realizaban otras tareas. La solución pasa por utilizar otros test complementarios a los Smoke Test que tengan otras características diferentes. ¿Cómo son estas pruebas a realizar? ¿Qué politica seguir para su utilización?

Un problema que surge una vez podría darse más veces. Esto es lo que llamamos regresión. Con esta premisa construiremos unos test que tratarán de asegurar estabilidad en el desarrollo: antes de cada release, antes de un cambio particularmente arriesgado, realizaremos unas pruebas que nos permitan tener un cierto nivel de seguridad de que no hemos desandado parte del camino. Estos test se generarán a partir de cambios que ocurrieron en el pasado: hitos, funcionalidades especiales, etc. Se trata de unos test de caja negra que pueden no identificar qué es lo que falla, con lo cual necesitaremos apoyarnos en otros tipos de test, como por ejemplo los unitarios. Son de grano grueso porque prueban funcionalidades completas, comprueban la integración entre diferentes partes, etc.

Los casos que componen estos test pueden ser antiguos fallos detectados por estos mismos test, fallos informados por clientes, fallos detectados por otros tipos de pruebas… Al final habrá un gran número de casos, que nos ayudarán a comprobar que el sistema avanza. Si el sistemas no pasa las pruebas habrá ocurrido una regresión. Nunca se eliminan casos de prueba de este tipo, porque los errores pueden volver. Debido a su tamaño, estos test podrían ejecutarse por la noche, que suele ser cuando menos carga de trabajo se da. Si es posible ejecutarlos en cada operación de protección o check-in (lo cual es complicado debido al tamaño que van adquiriendo  estos test) podremos ver exactamente el punto en el cual el sistema retrocede.

Private Versions

¿Cómo evaluar un cambio complejo, beneficiándose del control de versiones, sin hacer ese cambio público? Pongámonos en el caso de querer probar un cambio localmente, sin correr el riesgo de romper el sistema global. Para ello, podemos establecer checkpoints en ciertos períodos del camino de tal cambio. Recordar que cuando se sube un cambio al control de versiones, el resto de ítems asimila tal cambio al actualizar sus propias revisiones para formar nuevas versiones. Por tanto, es necesario proveer un mecanismo para hacer checkpoints de cambios con la granularidad adecuada que el desarrollador estime conveniente. Esto podría proveerlo un área local de control de revisiones. Sólo los cambios estables van a parar al control de versiones público. Este sistema debería proveer también de un modo de integración para esos cambios inestables con el estado actual del proyecto. El funcionamiento de este área local funcionará como el control de versiones: una vez estabilizados los cambios, éstos se integran en la línea principal del proyecto siguiendo las políticas adecuadas.

Puede utilizarse, para esto, un repositorio local, una rama privada para el desarrollador en cuestión, etc. Algunas herramientas software de gestión de configuraciones proveen mecanismos para esto mediante, por ejemplo, sistemas de permisos del estilo de ACLs (Access Control List o listas de control de acceso, que vigilan el acceso a determinado elementos de un todo; en este caso, artefactos generados en un proyecto y gestionados por el control de versiones). Lo importante es que el programador pueda trabajar cómodamente subiendo los cambios cuando él considere apropiado, sin entorpecer el trabajo de los demás.

Release Line

¿Cómo realizar el mantenimiento de versiones publicadas (releases) sin interferir con el trabajo propiamente de desarrollo? Debería aislarse lo uno de lo otro. Una versión de producción o release evoluciona de forma totalmente diferente a una de desarrollo, ya que necesita mejoras y mantenimiento. Los problemas surgen cuando se detecta un problema en la versión de desarrollo que hay que actualizar urgentemente en la versión de producción: las operaciones de fusión (merge) son complejas.

Para evitar estas situaciones, es recomendable mantener cada versión de release en una línea de producción diferente. Esto permite que tal línea avance a medida que se solucionan errores. Para ello,  se podría crear una rama (o repositorio) para cada versión de producción. Posteriormente se actualizarán las mejoras y repararciones realizadas en la rama o repositorio de cada release de forma periódica, mediante una operación de merge. La línea de desarrollo (línea principal) será la guía, la cual recibirá los resultados de los merge fruto de las mejoras realizadas release a release.

———————————————————-

Referencias:

Steven Berczuk, Brad Appleton: “Software Configuration Management Patterns, Effective Teamwork, Practical Integration

Anuncios

Alfred Wegener: el genio olvidado

Alfred Wegener

En estos días en los que todo el mundo recuerda a Charles Darwin como consecuencia del 200 aniversario de su nacimiento, no puedo evitar recordar a otro genio de similar talento pero mucho más olvidado: Alfred Wegener. ¿Y por qué no se recuerda a Alfred Wegener tanto como a Charles Darwin? Pues no sé, quizá sea menos mediático, a lo mejor es porque no era pelirrojo, como yo, o quizá fue menos polémico… quizá lo que ocurre es que no hizo rasgar las vestiduras de la Santa Madre Iglesia y por eso “mola” menos. No chana tanto porque no le plantó cara a los estamentos más profundos de la civilización… Ni siquiera la página dedicada a su persona en la Wikipedia en español está bien redactada. Y sin embargo, dejándonos de morbos, polémicas y tonterías, Wegener cambió la forma de ver el mundo tanto como Darwin, Copérnico, Galileo o el mismísimo Einstein. Pero en ciencia quizá existe también eso que llaman carisma o mojo, y ya se sabe lo que pasa: o se tiene o no se tiene.

Conocí a Wegener en los libros de Geología de la ESO, porque aunque no se lo puedan creer todavía se estudiaban a algunos genios en la ESO. Me llamó mucho la atención el personaje, me imaginé al ingenioso geólogo observando sus mapas, pipa en boca escudriñando con sus hábiles ojos como si tratara de resolver un puzzle que estuvo delante de nosotros siempre y no supimos resolver. Desde entonces le cogí cariño, porque jamás había oído hablar de él antes y jamás he vuelto a oir hablar de él después, porque sus descubrimientos cambiaron el mundo para siempre y porque no es tan mediático como Darwin. Que conste que no tengo nada en contra de Darwin; polémicos abstenerse.

Wegener, para el que no lo sepa, fue aquél geólogo berlinés que descubrió, a principios del siglo pasado, que la superficie terrestre no estaba formada por algo uniforme formando un todo, sino que estaba compuesta por piezas que encajaban de una cierta forma. Cada una de estas piezas constituía lo que se ha dado en llamar placa tectónica, y flotaba sobre una superficie de lava líquida que se conoce como magma. Antes, se pensaba que uno podría viajar en calesa desde Nueva York a Londres sin ningún problema por el fondo marino, salvando el obvio inconveniente de que no podemos respirar bajo el agua. Se pensaba que el fondo marino era algo así como una piscina, una planicie. Afirmar lo contrario le costó a Wegener muchas controversias, enemistades y burlas. Posteriormente se descubrió la brecha del Atlántico, una auténtica cordillera submarina que separa las placas Norte- y Sudamericana de las placas Euroasiática y Africana. Como para excalarlas en calesa y caer después a profundos pozos de lava, diría yo.

Más allá en sus investigaciones, justificó con su teoría el surgimiento de montañas formando cordilleras (orogénesis), islas, volcanes, terremotos, maremotos, fisuras y fallas. Así de importante es que hoy día utilizamos la escala Ritzer para medir la violencia de un terremoto sin pensar en quién descubrió por qué se producían. En su teoría, Die Entstehung der Kontinente und Ozeane, “El origen de los continentes y los océanos” (la analogía con Darwin y su origen de las especies es patente), explicó que en un principio todas las tierras formaban un único continente, Pangea. Quizás esta historia ya nos suene más a algunos que hayamos visto algunos documentales de la 2 y seamos o fuimos alguna vez unos apasionados de los dinosaurios.

Pregúntenle a un geólogo quién fue Wegener. Hoy día toda la geología se explica a partir de sus teorías, que comenzó con el estudio de fósiles y rocas. La plasticidad de los terrenos, los movimientos sísmicos y sus consecuencias, el pasado, el presente y lo que será de nuestra Madre, la corteza terrestre, en el futuro.

Wegener fue, sin duda, el padre fundador de una de las principales revoluciones científicas del siglo 20. Va por usted, maestro.

Patrones en Gestión de Configuraciones Software (IV)

Codeline Policy

Cuando se mantienen varias líneas base para un proyecto, surge el problema de cómo distinguir entre cada una de ellas, en cuál hacer las protecciones, qué pruebas ejecutar, cada cuánto tiempo se debería realizar una protección en esa línea, etc. Cada línea base tendrá un propósito diferente: desarrollo, depuración, soporte a distintas plataformas…; cada una con diferentes características en cuanto a estabilidad. Por ejemplo, una línea de versiones de producción será más estable que una de desarrollo.

Se hace necesario, por tanto, definir políticas para cada línea y comunicarlas al equipo. Para cada rama o línea base, se define una política que determina cómo y cuándo deben hacerse cambios en dicha rama o línea base. Tales políticas deben ser concisas y auditables. Para ello, utiliza nombres con significado y siguiendo una convención, formula un propósito coherente para dicha rama, como ya hemos indicado: tipo de trabajo que contiene, cómo y cuándo se protegen los artefactos (ítems o documentos: elementos sujetos a control de versiones), cuándo y cómo hacer una fusión (merge) , cuándo y cómo crear ramas, etc. Además, deben definirse unas restricciones de acceso, relaciones de importación y exportación con respecto a otras líneas base; tiempo de vida previsto para dicha línea; carga de actividad y frecuencia de integración esperada.

Todo ello se puede resumir en unos pocos párrafos, no hay porqué escribir un tratado. Tampoco es necesario indicar todos los puntos que hemos enumerado; solamente se incluirán aquellos que tienen sentido para cada caso concreto. Siempre que se pueda, ayúdate de tu software de control de versiones para realizar esta tarea.

Un corolario que se podría deducir de todo lo dicho hasta ahora sería lo siguiente: debería crearse una nueva rama cuando existan incompatibilidades entre políticas, puesto que cada rama define una política, la suya propia.

Smoke Test

¿Cómo verificar que el sistema seguirá funcionando después de realizar un cambio? Una solución es generar una serie de casos de prueba a ejecutar antes de realizar una protección en el repositorio. El problema surge ahora a la hora de pensar cómo deben ser tales test. Lógicamente no se puede probar todo porque sería demasiado pesado y el equipo tendería a realizar pocas protecciones; por tanto, un buen Smoke Test podría incluir pruebas de las partes más críticas o más propensas a errores (recordar la Ley del 80 y 20 de Pareto, que aplicada al software sería algo así como “el 20% del código aglutina el 80% de los errores“. No es difícil identificar ese 20%…). Verifica la funcionalidad básica: que la aplicación no se haya roto de un modo obvio. Automatiza tales pruebas, y que simplemente se informe de si el test se ha pasado o no. Posteriormente, veremos a mano dónde falló.

Un apunte muy importante es que los Smoke Test no son ni las pruebas de depuración ni las pruebas que realiza de su código el propio desarrollador. Tanto menos son pruebas unitarias, que se presentan a continuación, cerrando esta entrega.

Unit Test

¿Cómo verificar que un módulo (procedimiento, rutina, función, método o lo que quieran) sigue funcionando como debería después de realizar un cambio? Esto también sirve para nuevos módulos. Comprobar que el estado no cambia nos ayudará a mantener la estabilidad en nuestro desarrollo. Nos permite ver si hay errores y dónde, entre protección y protección, aumentando enormemente la visibilidad del progreso en el desarrollo.

Posteriormente, al realizar los test de integración o los Smoke Test (ver más arriba) es más sencillo saber qué parte falló. Desarrolla, pues, y ejecuta test unitarios que prueben de forma exhaustiva cada módulo.

Al igual que otros tipos de test estos son susceptibles de ser automatizados, lo cual es, además, muy recomendable. Son autoevaluables, de grano fino y aislados en cuanto a dependencias con otros módulos o pruebas. Deberían verificar el contrato entre interfaces (recordar programación por contrato). Debería ser, además, fácil de ejecutar.

Los test unitarios deben ser ejecutados durante la implementación del módulo y antes de hacer una protección. Pueden ser utilizados también cuando se detectan errores a un nivel superior: pruebas de sistema, de integración…

“Si no se pueden identificar pruebas unitarias para una unidad, deberías revisar tu diseño.”

Stephen Berczuk.

———————————————————-

Referencias:

Steven Berczuk, Brad Appleton: “Software Configuration Management Patterns, Effective Teamwork, Practical Integration

Por un lenguaje más “cool”

El otro día recibí uno de esos correos que se reciben de mucho en mucho tiempo, de los que merece la pena guardar y enmarcar. Hablaba de una carta que escribió una radioyente al programa de Luis del Olmo, en la que hablaba sobre el estado actual de nuestra querida lengua. Dado que yo soy un amante protector de nuestra lengua, aunque de vez en cuando la pellizque o meta una patada en sus partes en este blog, no pude evitar esbozar una sonrisa, no sólo por el ingenio de la autora, sino por mi apoyo total a su opinión. No sé si les gustará a mis pocos lectores, pero me parece algo digno de publicar. Por cierto, si tal radioyente lee esto, le pido tenga a bien facilitar su nombre, pues con mucho gusto lo pondré al final del artículo. Toda una sátira.

Desde que las insignias se llaman pins, los maricones gays, las comidas frías lunchs, y los repartos de cine castings, este país no es el mismo: ahora es mucho, muchísimo más moderno.

Antaño los niños leían tebeos en vez de comics, los estudiantes pegaban posters creyendo que eran carteles, los empresarios hacían negocios en vez de business, y los obreros, tan ordinarios ellos, sacaban la fiambrera al mediodía en vez del tupper-ware.

Yo, en el colegio, hice aerobic muchas veces, pero, tonta de mí, creía que hacía gimnasia.   Nadie es realmente moderno si no dice cada día cien palabras en inglés.   Las cosas, en otro idioma, nos suenan mucho mejor.   Evidentemente, no es lo mismo decir bacon que panceta, aunque tengan la misma grasa, ni vestíbulo que hall, ni inconveniente que handicap

Desde ese punto de vista, los españoles somos modernísimos. Ya no decimos bizcocho, sino plum-cake, ni tenemos sentimientos, sino feelings.

Sacamos tickets, compramos compacs, comemos sandwiches, vamos al pub, practicamos el rappel y el raffting, en lugar de acampar hacemos camping y, cuando vienen los fríos, nos limpiamos los mocos con kleenex.

Esos cambios de lenguaje han influido en nuestras costumbres y han mejorado mucho nuestro aspecto.   Las mujeres no usan medias, sino pantys y los hombres no utilizan calzoncillos, sino slips, y después de afeitarse se echan after shave, que deja la cara mucho más fresca que el tónico.

El español moderno ya no corre, porque correr es de cobardes, pero hace footing; no estudia, pero hace masters y nunca consigue aparcar pero siempre encuentra un parking.

El mercado ahora es el marketing; el autoservicio, el self-service; el escalafón, el ranking y el representante, el manager.  Los importantes son vips, los auriculares, walkman; los puestos de venta, stands; los ejecutivos, yuppies; las niñeras, baby-sitters y hasta nannies, cuando el hablante moderno es, además, un pijo irredento.

En la oficina, el jefe está siempre en meetings o brain storms, casi siempre con la public-relations, mientras la assistant envía mailings y organiza trainings; luego se irá al gimnasio a hacer gim-jazz, y se encontrará con todas las de la jet, que vienen de hacerse liftings, y con alguna top-model amante del yoghourt light y el body-fitness.

El arcaico aperitivo ha dado paso a los cocktails, donde se hartan a bitter y a roast-beef que, aunque parezca lo mismo, engorda mucho menos que la carne.

Ustedes, sin ir más lejos, trabajan en un magazine, no en un programa.   En la tele, cuando el presentador dice varias veces la palabra O.K. y baila como un trompo por el escenario la cosa se llama show, bien distinto, como saben ustedes, del anticuado espectáculo; si el show es heavy es que contiene carnaza y si es reality parece el difunto diario El Caso, pero en moderno.

Entre medias, por supuesto, ya no ponen anuncios, sino spots que, aparte de ser mejores, te permiten hacer zapping.

Estas cosas enriquecen mucho.

Para ser ricos del todo, y quitarnos el complejo tercermundista que tuvimos en otros tiempos, solo nos queda decir con acento americano la única palabra que el español ha exportado al mundo: la palabra “SIESTA.”

No se puede estar más de acuerdo.

Patrones en Gestión de Configuraciones Software (III)

Integration Build

Objetivo: asegurar que el código base del sistema siempre se podrá construir (compilar y linkar) al menos, sin que en ello influyan los espacios de trabajo privados. ¿Cómo saber si el trabajo realizado por un individuo encajará bien en lo realizado por el resto? ¿Y si no es posible que uno compile todo el proyecto localmente antes de hacer un check-in? Para solventar este problema, hay que asegurarse de que todos los cambios y sus dependencias son construidos utilizando un proceso centralizado de integración. Este proceso deberá ser: reproducible, tan similar como sea posible a la construcción final (como producto; un candidato a pruebas) automatizado para cada check-in, y finalmente un sistema de identificación y localización rápida de errores e inconsistencias.

La frecuencia de esta integración dependerá del tamaño de la construcción y de la velocidad con la que ocurren cambios. Lo normal es que sea entre una vez diaria y una por cada check-in. Es conveniente identificar tales compilaciones en el control de versiones con una etiqueta.

Third Party Codeline

¿Cuál es el modo más efectivo de coordinar versiones de código propio con COTS o código subcontratado? Los COTS evolucionan de versión en versión de forma independiente a como lo hace el código propio. ¿Qué versiones relacionar de cada COTS con qué versión del producto? Para ello, crea un codeline independiente para COTS. Crea espacios de trabajo y kits de instalación para tal COTS a partir de su codeline. Esto sería útil para, por ejemplo, si pudieras tocar el código de estos componentes externos y adaptarlo a tu antojo. Cada cambio, una nueva revisión, abriendo una rama independiente para tal cuestión (patrón Task Branch, que ya veremos más adelante).

Cuando el proveedor saca una nueva versión de estos componentes se hace un merge de la nueva versión y los cambios realizados por la propia organización. Cuando se publique un nuevo release del producto, se etiqueta tal release y se etiqueta la revisión del COTS en la rama de cambios creada con la versión de nuestro producto con la cual se corresponde.

Es necesario hacer que el control de versiones permita descargar la revisión adecuada de cada COTS de manera transparente cuando uno descarga el código propio, y que al realizar un check-in haga lo correspondiente con tal COTS (check-in de los cambios realizados en el COTS).

Task Level Commit

¿Cuánto trabajo debería realizarse entre check-in y check-in al control de versiones? ¿Cuánto se debería esperar para subir cambios? Es decir: lo que aquí nos preocupa es la granularidad de las tareas a realizar. De esto depende el que el historial de revisiones de un artefacto nos muestre, de forma fidedigna, la evolución de tal artefacto. Hay que tener en cuenta los costes de hacer muchos check-in (tests, construcciones, integraciones, verificaciones…) y de hacerlos con menos frecuencia (mayor probabilidades de colisiones con el código de otros programadores, código desactualizado, mayor esfuerzo a la hora de realizar merges). Las tareas deben ser tan atómicas como sea posible, y que los cambios afecten al menor número posible de ficheros, para así poder identificar de forma sencilla qué cambios se corresponden con qué tarea.

Resumiendo: tareas de grano fino, un check-in por tarea. En caso de que la tarea sea demasiado grande (por su naturaleza, o por lo que sea) y por tanto no se pueda romper, habría que tener en consideración el crear una rama independiente para tal tarea (nuevamente volvemos a citar el patrón Task Branch, del que hablaremos más adelante).

———————————————————-

Referencias:

Steven Berczuk, Brad Appleton: “Software Configuration Management Patterns, Effective Teamwork, Practical Integration