spinner

A veces uno de los mayores problemas a la hora de gestionar el código de nuestras aplicaciones es la variedad de tecnologías empleadas en el desarrollo, otras veces es que la gran cantidad de código y proyectos se ha vuelto poco gestionable. Pues bien, una herramienta que nos permite solucionar todos estos problemas y muchos más es Bazel.

¿Qué es Bazel?

Bazel es una herramienta de construcción de software liberada por Google que nos permite ser mas rápidos, escalables y fiables a la hora de crear nuestro software. Bazel esta concebido con la idea de la creación de «monorepos» con gran diversidad de lenguajes y herramientas para la unificación del código y simplificar multitud de tareas en nuestro desarrollo.

Algunas de las principales ventajas que nos ofrece Bazel son:

1. Velocidad: Bazel realiza tareas de forma paralela y además dispone de un sistema de caché el cual evita realizar operaciones innecesarias. No ejecuta builds ya lanzados, ni tampoco lanza test que no han sufrido modificación alguna.

2. Múltiples lenguajes: Bazel permite crear aplicaciones aunando en un mismo proceso de construcción multitud de tecnologías, como pueden ser Java, C++, python. Go, …

3. Escalabilidad: Bazel puede manejar grandes cantidades de información en múltiples repositorios o en un único monorepo.

4. Extensible: dispone de extensiones para dar soporte a nuevos lenguajes de programación así como una comunidad en crecimiento debido a sus ventajas.

¿Cómo funciona Bazel?

Bazel se organiza a partir de un directorio raíz denominado workspace. Este debe contener un fichero denominado «WORKSPACE» en el cual se cargan las dependencias externas del proyecto como repositorios GIT, un proyecto maven con librerías u otro workspace del que depende nuestro proyecto.

A partir del workspace todo el código se organiza en paquetes, los cuales, deben contener un fichero «BUILD». Los ficheros de Build son un conjunto de reglas en lenguaje imperativo que definen cómo se construye ese pequeño paquete de nuestro producto. En las reglas del Build se pueden declarar los ejecutables que vamos a necesitar, por ejemplo java_binary; los test que se van a ejecutar (ej: java_test); así como las librerías que se pueden usar en ese mismo paquete o desde fuera (ej:java_library). Todas estas reglas deben tener un name para poder referenciarlas.

La mayor parte de las reglas declaradas en los Build puede tener: srcs (código fuente), deps (dependencias del módulo) y data (ficheros o datos que nuestro código necesita), pero en función de la regla puede existir otra serie de atributos como resources, visibility, etc.

Aparte de los ficheros de Build, Bazel dispone de ficheros .bzl en los cuales podemos definir reglas e importarlos mediante la etiqueta load. Además, esta herramienta prefiere gestionar las dependencias desde repositorios externos cargados desde el «WORKSPACE», pero en caso de ser necesario cargarlas internamente es recomendable que las dependencias se encuentren bajo un directorio llamado /third_party con un fichero «BUILD» a partir del cual se declaren las dependencias para ser importadas.

Tomando como ejemplo la siguiente estructura de proyecto.

Proyecto

|– proyecto_java1

|    |– src …

|    |– BUILD

|– proyecto_go1

|    |– main.go

|    |– BUILD

Comandos útiles

Algunos de los comandos básicos para trabajar con Bazel son:

  • Build, construye el target o targets seleccionados
    Por ejemplo, bazel build //proyecto_java1:generate_jar, costruye el target generate_jar el cual genera el artefacto del paquete.
  • Test, ejecuta todos los test definidos en el target, aunque es posible ejecutar test concretos con los argumentos de que dispone. Ejecución de todos los test contenidos en un paquete
    bazel test //proyecto_go1/…:all
  • Run, como su propio nombre indica, ejecuta la regla o reglas que aplican al comando.
  • Query, que nos permite buscar gran cantidad de información/ficheros en nuestro proyecto, desde las dependencias dependencias de una regla hasta comprobar si existen determinados ficheros o directorio.Por ejemplo: bazel query “deps(//proyect_java1)”, con lo que buscaríamos todas las dependencias de nuestro proyecto java.

Aparte de las herramientas core incluidas en la herramienta, existen extensiones por lenguaje para hacerlo más extensible como, por ejemplo, en el caso de nodejs, en el cual se añaden funcionalidades propias del lenguaje, como es el caso de la gestión de dependencias con yarn o npm.

Conclusiones

En mi caso particular, inicialmente el uso de Bazel para crear un «monorepo» donde aunar un stack completo de microservicios y multitud de herramientas, generaba cierta incertidumbre tendiendo a pensar en que volvíamos a la idea del monolito. Pero en la fase de salida a entornos productivos en la que nos encontramos actualmente, la decisión no ha podido ser mejor.

Principalmente a la hora de desarrollar cualquier módulo o funcionalidad es mucho mas cómodo y rápido que tener que abrir multitud de repositorios para una única funcionalidad. La homogeneidad conseguida al tener todo unificado es muy alta. Como mayor inconveniente encontrado a la hora del uso de Bazel resaltaría la complejidad a la hora de usar versiones,  pues es un problema complejo de atajar cuando necesitas convivir versiones de cada una de las piezas.

Y por último, resaltaría la agilidad que aporta a las tareas de deploy el tener un único repositorio con Bazel, ya que el hecho de tenerlo todo junto permite realizar por ejemplo un cambio que aplique a todos los microservicios muy rápidamente.

Imagen: pexels.com

Las opiniones vertidas por el autor son enteramente suyas y no siempre representan la opinión de BBVA Next Technologies.

¿Quieres saber que más cosas hacemos en BBVA Next Technologies?