Nuevo proyecto: de regreso a .Net

Nuevo proyecto: de regreso a .Net

Hace un par de semanas comencé un nuevo proyecto, esta vez con tecnología .Net y con un contexto interesante:

  • El proyecto es de desarrollo y mantenimiento evolutivo de un sistema existente
  • El sistema está compuesto por un conjunto de aplicaciones de distinto tipo
  • El sistema lleva varios años en producción y no tiene ningún tipo de pruebas automatizadas
  • La documentación funcional y técnica es escasa (prácticamente nula)
  • Varias de las bases de datos no tienen claves foráneas
  • El sistema escala en forma vertical y no horizontal

En este contexto voy estar colaborando en cuestiones de arquitectura, operaciones y prácticas técnicas de desarrollo.

Continuará…

Fin de proyecto

Fin de proyecto

Luego de 3 intensos meses esta semana terminé mi segundo proyecto inmersivo del año.  Me sumé al equipo para dar una mano con cuestiones de arquitectura y operaciones, pero como de costumbre, al trabajar inmersivamente con el equipo uno siempre termina en medio en otras cuestiones. En estos tres meses además de completar diversas funcionalidades también hicimos lo siguiente:

  • Corrimos pruebas de performance y en base a ellas hicimos diversos ajustes en la aplicación.
  • Diseñamos e implementamos una infraestructura escalable basada en AWS
  • Implementamos un proceso de despliegue automatizado basado en CodeDeploy
  • Instrumentamos la aplicación para posibilitar su monitoreo con New Relic y CloudWatch
  • Pasamos de un esquema de trabajo basado en iteraciones de 2 semanas a un esquema de entrega continua liberando funcionalidad tan pronto como se completa

En verdad disfrute mucho ser parte del equipo y estoy muy conforme con el trabajo realizado.

 

Hacia un esquema de entrega continua

Cuando me uní al proyecto el equipo venia trabajando con un esquema ágil «clásico» pero con algunas particularidades:

  • Iteraciones time-boxed de 2 semanas
  • Congelamiento y regresión/estabilización los últimos dias de la iteración
  • Pruebas unitarias automatizadas
  • Regresión manual
  • Release al final de la iteración

Esta forma de trabajo ocasionaba de vez en cuando que se llegara al final de iteración con funcionalidades «inestables» porque no se llegaba a tiempo a completar la regresión/estabilización. Personalmente ya me había cruzado con este tipo de situaciones en el pasado y por ello en una retrospectiva propuse al equipo hacer un experimento en la siguiente iteración: tomar una user story, implementarla, testearla y releasearla apenas se la termine sin esperar al final de la iteración. De esta forma:

  • El flujo de entrega sería más frecuente bajando la ansidad de cliente
  • Obtendríamos feedback más temprano del usuario
  • El esfuerzo de estabilización estaría distribuido a lo largo de toda la iteración (y posiblemente sería menor en términos generales)
  • Se evitaría el incremento del WIP (work in progress) ya que las user stories completas no quedarían esperando al final de la iteración para su release.

Cuando hice esta propuesta algunos miembros del equipo me miraron con cierta desconfianza pero a pesar de eso todos estuvimos de acuerdo en hacer el experimento.

Continuará…

Nuevo proyecto: Python, iPhone y Amazon

Hace un par de semanas me sumé a un nuevo proyecto para colaborar en cuestiones de operaciones/arquitectura. Cuando me hicieron la propuesta para sumarme no lo dudé ni un momento pues el contexto me resultó super interesante:

  • Plataforma multi-tenant de administración de contenido + aplicación móvil para consumo del contenido
  • Integración con redes sociales
  • Características de red social (contenido social compartido, picos de carga, potencial de cientos de miles de usuarios, etc)
  • Frontends Angular y iPhone
  • Backend Python
  • Infraestructura Cloud en Amazon
  • Equipo de 12 personas incluyendo devs, un especialista en ux, un tester y un coach (y ahora yo en rol de devop)
  • Cliente remoto (en US) y equipo de ingeniería distribuido en 3 cuidades.

Continuará…

 

Automatización de test en proyecto «brown-field»

Hace poco más de un mes comencé a trabajar en un proyecto de desarrollo de una aplicación existente (brown-field project). La aplicación en cuestión era un monolito construido con una antigua versión de Grails y luego actualizado a Grails 2.5 y con no más de 30 pruebas unitarias. La visión del proyecto tenia 2 objetivos bien claros (sin ningún orden particular):

  • Realizar una rearquitectura de la aplicación para llevarla a un esquema de microservicios
  • Agregar un conjunto de funcionalidades relacionadas principalmente a cuestiones de integración con otras aplicaciones.

Una de mis primeras propuestas para el Product Owner fue comenzar generando un conjunto mínimo de pruebas end-2-end de regresión que nos dieran cierta seguridad para poder realizar modificaciones sobre la aplicación sin sonarla. El Product Owner siendo una persona de formación técnica estuvo de acuerdo con la propuesta y hacia allí fuimos. Comenzamos identificando las funcionalidades más críticas para el negocio y diseñando una arquitectura de pruebas para ellas.

Dado que la aplicación no había sido desarrollada con la «testeabilidad» en mente, resultaba muy dificil realizar pruebas que no unitarias y de API, asi que decidimos ir por un enfoque de caja negra realizando pruebas end-2-end que interactuaran con la aplicación via UI tal cual un usuario real.

Luego de un de par de pruebas de concepto llegamos a la siguiente arquitectura de tests:

test_arq

  • Gherkin: la idea de usar este DSL user-friendly no tiene un fundamento en que nos permite espeficar flujos funcionales desde una perspectiva de negocio.
  • Cucumber-JVM: como la aplicación está hecha en Grails el cual ya corre sobre JVM y los nuevos servicios se construirán en Java, nos pareio que Cucumber-JVM era la opción obvia. si es cierto que también podríamos haber usar JBehave, la realidad es que Cucumber-JVM está más difundido y por ello hay más documentación de soporte.
  • DB-Unit: lo utilizamos para cargar distintos set de datos para cada contexto de tests. Asimismo DBUnit se encarga de borrar los datos existentes antes de cargar cada dataset.
  • Selenium: era la opción default para interactuar con aplicaciones web. Para generar cierto nivel de abstracción sobre el webdriver utilizamos PageObjects.
  • JUnit: finalmente tenemos JUnit como librería de aserciones y motor de ejecución.

Continuará…

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.