3 semanas de proyecto

El miércoles pasado se cumplieron 3 semanas/iteraciones desde que empecé a trabajar en mi proyecto actual. En estas semanas creo que hemos hecho algunos avances importantes respecto al producto y a la forma de trabajo:

  • Mejoramos “el teamwork”
    • ahora el equipo se sienta todo junto en la misma mesa
    • dimos un par de pasos para integrar a los miembros con asignación part-time (testing y UX)
    • creamos una lista de correo que tiene bastante vida y que nos permite estar más comunicados principalmente los días que hacemos homeworking
  • Respecto de los aspectos técnicos del proceso de desarrollo
    • tenemos 2 ambientes de testing: uno para tests automatizados y otro para tests manuales
    • agregamos tests automatizados
    • automatizamos el deployment a los distintos ambientes
    • empezamos a desplegar la aplicación en forma frecuente
    • ordenamos el versionado del producto agregando release notes y tags en los repositorios
  • Finalmente respecto del producto
    • hicimos un refactoring de arquitectura que gradualmente nos permite movernos de una arquitectura monolítica a una esquema de microservicios
    • como consecuencia del refactoring mejoró la estabilidad del producto

Personalmente estoy contento con como venimos avanzando y con los desafios que aún nos quedan por delante.

Continuará…

Nuevo proyecto: java, micro-servicios y entrega continua

Esta semana empecé a trabajar en un nuevo proyecto. Se trata de una aplicación que una empresa desarrolló para uso interno hace ya varios años y que ahora quiere”productizarla” para ofrecerla a terceros y montar un esquema de Software As A Service. Es ahí donde entro yo para dar una mano en “la productización” colaborando en cuestiones de refactoring de arquitectura, tareas de automatización, monitoreo y ajustes en el proceso de desarrollo.

La aplicación nació como un monolito Grails que fue evolucionando a lo largo de los años incorporando nuevas funcionalidades, en este momento está corriendo sobre Grails 2.5. Recientemente se decidió pasar a una arquitectura de micro-servicios construidos con Spring Boot sobre Java 8.

Más allá de las cuestiones técnicas, hay algunos issues a nivel de dinámica de trabajo. Si bien el equipo trabaja en un esquema tipo Scrum, el testing de la aplicación lo realiza otro sector de la empresa y es testing manual. Esto genera importantes delays en el release, ya que muchas veces se llega al final de la iteración y no se puede pasar a producción pues hay funcionalidades no testeadas.

Continuará…

Nuevo proyecto de CI / CD

Hacía fines de diciembre del año pasado me contactaron de una empresa para que los ayudara con una iniciativa de Integración Continua. Intercambiamos un par de mails, tuvimos una llamada telefónica de 20 minutos y acordamos una primera reunión.

Desde mi punto de vista la práctica de integración continua no es un fin en sí mismo sino que es una herramienta. Por ello, una de las primeras cosas que pregunté fue: ¿Cuál es su problema? ¿Qué esperan solucionar con esta práctica? La respuesta fue clara, había dos problemas principales: por un lado la calidad del producto y por otro el desperdicio de tiempo invertido en el despliegue de la aplicación a los distintos ambientes. Esto me permitió entender que la cuestión iba bastante más allá de la integración continua.

El desafió me pareció muy interesante, pues se trataba de una empresa grande, con equipos distribuidos geográficamente en distintos lugares de latinoamérica y cuyo negocio estaba centrado en la venta de soluciones de banca. La empresa contaba con un plataforma de productos que vendía a distintos clientes y cada venta implicaba una implementación/customización de los productos. De esta forma había equipos trabajando sobre los productos y en paralelo un equipo de implementación para cada cliente que compraba el producto.

En términos de administración de la configuración a primera vista la naturaleza del negocio llevaba a tener un trunk de producto con un branch por cada cliente. Fácil decirlo, no tan fácil de implementarlo sobre todo teniendo el código de todos los productos en un único repositorio CVS.

La plataforma tecnológica era Java, un clásico en sistemas de banca. Pero dado que la solución se vendía a distintos clientes la aplicación debía funcionar sobre distintos ecosistemas tanto a nivel base de datos, application server y service bus.

Un último condimento no menor es que gran parte del código venía arrastrándose desde hacía varios años y tenía dependencia con componentes open source ya sin soporte (por ejemplo Apache Hivemind). Adicionalmente también se estaban usando versiones viejas de algunas herramientas (por ejemplo java6  y maven2).

Cabe destacar que varias de este cuestiones no las sabia inicialmente, sino que las fui descubriendo una vez empecé a trabajar.

La situación era compleja y las expectativas del cliente eran muy grandes. Mi propuesta fue simple y concreta:

Busca un equipo que esté interesado en trabajar en esta iniciativa. Yo me siento y trabajo con el equipo codo a codo 1 mes para resolver los impedimentos que el equipo considere le impiden construir software de calidad.

Al cliente, le gustó la propuesta y la bola empezó a rodar.

Continuará…

Real-world TDD

El viernes pasado me tocó trabajar en una funcionalidad relativamente simple. Dada una aplicación de gestión de proyectos que tiene la capacidad de recibir mails (chiliproject) me pidieron que le agregue la capacidad de rutear los mails de entrada a uno u otro proyecto dependiendo de cierto algoritmo. Cuando estaba por empezar a desarrollarla pensé: “este es un excelente ejemplo para mostrar el uso de TDD en un contexto real”. Entonces abrí mi software de grabación y me puse trabajar. El resultado es este video que muestra cómo resolví parte del problema usando TDD. Espero lo disfruten.

Estrategia de Continuous Delivery: git + jenkins + heroku (parte 1)

En uno de los proyectos open source en los que participo hemos montado una infraestructura de continuous delivery que nos viene dando buenos resultados por ello quiero dedicar algunas líneas a describir su estructura.

El proyecto consta de 2 aplicaciones, una webapp y un servicio de backend pero cada una es manejada de forma independiente, por ello el resto de este artículo hablaré de forma genérica como si fuera una única aplicación. La aplicación web está construida con Ruby/Padrino, mientras que el servicio es Ruby “puro”.

El código de la aplicación es versionado en GitHub, donde tenemos un branch develop sobre el trabajamos y un branch master que tiene el código que está en producción. El código de develop es desplegado automáticamente por Jenkins en una instancia de prueba (que denominamos preview) mientras que el código de master es desplegado en la instancia de producción. Ambas instancia estan corriendo en Heroku.

En el proyecto somos tres programadores, que trabajamos en la aplicación en nuestros tiempos extra laborales. Cuando trabajamos en funcionalidades no tan chicas, que pueden llevarnos un par de días, creamos feature branches.

Adicionalmente al repositorio de GitHub, tenemos otro repositorio, privado, hosteado en BitBucket donde almacenamos la configuración de la aplicación. Aquí tenemos dos branches, uno por cada ambiente.

Jenkins se encarga de la correr la integración continua y los pasajes entre ambientes. Para ello tenemos los siguientas tareas:

  • CI: corre integración contínua sobre el branch develop. Básicamente corre pruebas unitarias y de aceptación.
  • CI_All: es análogo al CI, pero trabajo sobre todos los demás branches, lo que nos permite tener integración contínua incluso cuando trabajamos en feature branches.
  • Deploy_to_Preview: despliega en el ambiente de preview (test) el código proveniente del branch develop
  • Run_Preview_Pos_deploy: aplica la configuración actualizada y las correspondientes migraciones en el ambiente de preview y ejecuta un smoke test
  • Run_Pre_Production_deploy: mergea el branch develop en master
  • Deploy_to_production: despliega en el ambiente productivo el código proveniente del branch master
  • Run_Production_Pos_deploy: aplica la configuración actualizada y las correspondientes migraciones en el ambiente de preview y ejecuta un smoke test

Algunos detalles más para destacar son:

  • Todo commit a develop, se despliega automáticamente a preview.
  • No se hacen despliegues a preview y producción en forma manual, todo pasa por Jenkins
  • Ante cada build, Jenkins informa el resultado via chat (Jabber) a todos los miembros del equipo de desarrollo.
  • El monitoreo de la aplicación en el ambiente productivo lo hacemos con LogEntries y New Relic

Sin duda que este esquema no es una bala de plata, pero nos funciona muy bien en este proyecto. Algunas cuestiones a revisar en otros proyectos serían:

  • En caso de trabajar con un lenguaje compilado estáticamente (Java, C#, etc) habría que considerar utilizar un repositorio de binarios para que la aplicación se compile una única vez (algo tipo Artifactory)
  • En caso de aplicaciones con requerimientos de disponibilidad 7×24, había que considerar una estrategia de deploy distinta, tal vez algo del tipo blue-green deploy.

Si gustan conocer más detalles, no duden en consultar.

Configuration Management in my .NET project

On these days I am working on big system that is built on several components: a couple of websites, some backend services and several shared libraries. At this moment all these components are stored in the same Subversion repository. As you can image, this is a huge repository. At the same time, the system is already running in production mode and some components are being updated/replaced. Because of this situation and in order to simplify configuration management I being working on designing a new configuration management strategy. The solution I have in mind includes 3 key tools:

  • Git: to store source code, in particular I like GitHub service
  • NuGet: a package manager for .NET components (for those not familiar with .NET, it is like a Maven in Java o npm in Node). We know we will need a private Gallery for some components, but we still don’t decide if  we are going to host our own instance or if we will use a cloud service.
  • Jenkins: to provide continuous integration and deployment automation

Now I will try to explain how these components fit together.

Each shared library has its own Git repository. In case a library depends on third party component, it should declare the dependency package.xml for Nuget to download the dependency at build time. At the same time, stable versions of share libraries are published in NuGet Gallery (possible a private one).

Each application has its own Git repository. In case an application depends on third party component, it should declare the dependency in package.xml for Nuget to download the dependency at build time. In case this application depends on a  private share library there two options:

  • If the shared library will be used as it is, then it is managed with NuGet
  • If the shared library needs to be modified, then the shared library repository is added as a submodule of the application repository. This way you will be able to add the shared library project to your Visual Studio solution and will be able to modify it.

At the same time, Jenkins will monitor all repositories and trigger a CI job to compile and run automated tests on each change. For each application there will be additional Jenkins Jobs to perform deployments to the different environments. For each shared library, there will be a Jenkins job to publish the library to the NuGet repository.

scm

Los dilemas del uso de Gherkin/Cucumber

Como mencioné anteriormente, estoy trabajando un proyecto ocupando el rol de Software Engineer in Test (SET). Una de las primeras cuestiones que debí resolver en el proyecto fue acordar con los analistas/testers la convenciones para escribir los tests con Gherkin. Todo aquel que haya trabajado con Gherkin en algún momento seguramente se ha enfrentado a los diversos dilemas que plantea el uso de la herramienta.

Dilema 1

  • Escribir steps con parámetros, que permiten un alto reuso de steps definitions disminuyendo la cantidad necesaria de glue-code
  • Escribir steps sin parámetros, que permiten un mayor nivel de abstracción y expresividad, pero que al mismo tiempo requieren de más glue-code

Dilema 2

  • Agrupar los steps definitions por feature, lo cual posiblemente genere muchos archivos de steps definitions y también repetición de glue-code, pero que facilita la trazabilidad entre los archivos de features y los archivos de steps definitions
  • Agrupar los steps definitions por concepto de negocio, lo cual ayuda a reducir la cantidad de archivos y la posible repetición de código, pero que puede dificultar la trazabilidad entre archivos de features y archivos de steps definitions

Dilema 3

  • Hacer steps “stateless” (que no dependen de datos de contexto) lo cual permite mayor reuso, pero que obliga a pasar la información de contexto como parámetro de los steps
  • Hacer steps “stateful” (que sí dependen de datos del contexto) lo cual permite generar steps más claros yreducir/omitir los parámetros en los steps, pero que disminuye el reuso de steps y obliga a generar código adicional para el manejo del contexto

En todos los dilemas planteados existen más alternativas que las mencionadas, de hecho las alternativas aquí planteadas son en cierto modo extremas pero sirven para ejemplificar algunas de las cuestiones que debemos definir al trabajar con esta herramienta.

Al mismo tiempo hay algunas cuestiones adicionales a considerar dependiendo de la implementación de cucumber que utilicemos. Por ejemplo: la implementación Ruby es un DSL que permite fácilmente compartir estado entre los steps definitions independientemente de la forma en que los agrupemos, mientras que en la implementación Java (Cucumber-JVM) sólo es posible (en principio) compartir estado entre los steps definitions que esten agrupados en la misma clase.

Todas estas cosas son las que estuve definiendo la primer semana de proyecto. Ahora estoy trabajando en generar un “micro framework” (básicamente algunas clases helpers) que me faciliten la implementación de los steps definitions.

Continuará…