Desmitificando monorepos con Turborepo

Desmitificando monorepos con Turborepo

En los últimos años, el término "monorepo" ha ganado cierta relevancia y quizás tú, al igual que yo hace un tiempo, has escuchado hablar de monorepos pero no entiendes exactamente lo que significa.

A medida que aumenta la complejidad de los proyectos de desarrollo de software, encontrar formas eficientes de gestionar el código y colaborar se vuelve esencial. En este contexto, los monorepos emergen como una solución poderosa, un concepto que implica la gestión de varios proyectos en un solo repositorio.

En este artículo, además de explorar el concepto de monorepo, vamos a presentar Turborepo, una herramienta que simplifica aún más la gestión y construcción de proyectos en monorepos.

¿Qué es un Monorepo?

Un monorepo (abreviatura de "repositorio monolítico") es un repositorio de código que contiene múltiples proyectos, módulos o paquetes bajo un mismo techo. Esto contrasta con el enfoque tradicional de "multirepo", donde cada proyecto tiene su propio repositorio. En el contexto de un monorepo, varias partes del software se desarrollan, versionan y gestionan juntas en un solo repositorio, ya que estos proyectos suelen estar relacionados y compartir dependencias comunes, lo que facilita su mantenimiento en un solo lugar.

Beneficios y desafíos de los Monorepos

Para que tengas una visión más amplia sobre el tema, vamos a enumerar algunos puntos positivos del uso de monorepos:

  1. Compartición de Código y Dependencias: Los monorepos facilitan compartir código y dependencias entre proyectos relacionados. Esto reduce la duplicación de código, manteniendo una base de código cohesiva y constantemente actualizada.
  2. Refactorización y Cambios a Gran Escala: Hacer cambios que afectan a múltiples proyectos es más fácil en un monorepo, ya que las modificaciones se pueden aplicar de manera consistente en toda la base de código. Esto es especialmente beneficioso cuando se trata de refactorizaciones o actualizaciones de bibliotecas.
  3. Integración Continua Simplificada: Mantener un flujo de integración y entrega continua es más eficiente en un monorepo, ya que las dependencias compartidas se actualizan simultáneamente, reduciendo los conflictos de versiones.
  4. Mejor Comunicación: Los equipos que trabajan en proyectos relacionados pueden comunicarse de manera más efectiva, ya que todo el código está en un solo lugar. Esto también puede llevar a una resolución más rápida de problemas y a un intercambio más ágil de mejores prácticas.
  5. Gestión de Versiones Simplificada: Compartir dependencias entre proyectos significa que todas las partes del sistema pueden usar las mismas versiones de bibliotecas, evitando conflictos de versiones y mejorando la consistencia.

Sin embargo, no todo es perfecto. También existen ciertos puntos a tener en cuenta al usar un monorepo:

  1. Complejidad Inicial: Configurar un monorepo requiere una planificación cuidadosa y la elección de las herramientas adecuadas. La complejidad de la configuración puede ser un obstáculo inicial.
  2. Gestión del Tamaño: A medida que el monorepo crece, puede volverse voluminoso y difícil de navegar. Las estrategias de organización, modularización e indexación se vuelven esenciales.
  3. Conflictos de Fusión (Merge): Los conflictos de fusión pueden volverse más complejos en un monorepo, especialmente cuando varios equipos contribuyen a diferentes partes del código.
  4. Impacto de los Cambios: Una única modificación puede afectar a múltiples proyectos, lo que requiere pruebas exhaustivas y validaciones para garantizar la estabilidad.

Es por eso que es importante utilizar herramientas que ayuden a gestionar este tipo de repositorios, y aquí es donde entra Turborepo. Aunque existen varias herramientas disponibles, actualmente Turborepo ha ganado bastante relevancia.

Creando un Monorepo con Turborepo

Según la propia documentación de Turborepo, es un sistema de compilación inteligente optimizado para proyectos en JavaScript y TypeScript.

Una de sus principales características es su capacidad para almacenar en caché las salidas de compilación. Esto significa que si realizas un cambio en un proyecto, Turborepo reconstruirá solo el proyecto afectado y aquellos que dependan de él. Esto puede acelerar significativamente los tiempos de compilación, especialmente en monorepos grandes.

Turborepo también ofrece soporte para compilaciones en paralelo, lo que permite aprovechar completamente el poder de procesamiento de tu computadora. Esto puede acelerar aún más los tiempos de construcción y facilitar el trabajo con monorepos extensos.

Para empezar a usar Turborepo, puedes ejecutar el paquete create-turbo, que creará y configurará un proyecto en una carpeta que definas, usando el gestor de paquetes de tu preferencia:

npx create-turbo@latest

Una vez hecho esto, tendrás una estructura similar a esta:

Estrutura de pastas geradas pelo Turborepo

Puedes notar las carpetas apps y packages, y las subcarpetas dentro de ellas. Cada una de estas subcarpetas (docs, web, eslint-config-custom, tsconfig y ui) son proyectos separados (conocidos como workspaces), con sus respectivos archivos package.json y algunos con tsconfig.json y eslintrc.js.

La diferencia entre ellos es que los proyectos en la carpeta packages son paquetes globales utilizados por los proyectos en la carpeta apps. Turborepo ya crea algunos ejemplos, como los paquetes de configuración de ESLint y TypeScript, además de ui, que sería un paquete de componentes globales. Entonces, en la carpeta apps, se encuentran las aplicaciones propiamente dichas, como docs y web, que por defecto son aplicaciones Next.js.

Compartición de Código entre Proyectos

Aquí ya se puede ver lo fácil que es crear varias aplicaciones diferentes con las mismas configuraciones y componentes. Esta capacidad es uno de los mayores beneficios de un monorepo, ya que promueve la reutilización de código y el mantenimiento consistente. Las modificaciones se pueden aplicar en un solo lugar y reflejarse en todos los proyectos dependientes. Vamos a explorar cómo se logra esto utilizando el ejemplo de un componente de botón del paquete ui.

Ya tenemos un ejemplo de esto en los archivos generados por Turborepo. Dentro del paquete ui, tenemos algunos componentes, como Button y Header, que son utilizados en las aplicaciones docs y web.

// packages/ui/Button.tsx
"use client";

import * as React from "react";

export const Button = () => {
  return <button onClick={() => alert("boop")}>Boop</button>;
};

// packages/ui/Header.tsx
import * as React from "react";

export const Header = ({ text }: { text: string }) => {
  return <h1>{text}</h1>;
};

Aquí está la magia: al hacer cambios en el componente de botón dentro del paquete ui, esos cambios se reflejarán en todos los proyectos que utilizan ese componente. Esto reduce la complejidad de mantenimiento y garantiza la consistencia de la interfaz de usuario en todo el monorepo.

Además, compartir código entre proyectos usando paquetes también implica manejar las dependencias. Turborepo facilita esta tarea al permitirte actualizar las dependencias compartidas de manera coordinada.

Ejecutando los proyectos

Con la estructura ya creada, podemos ejecutar los workspaces usando los comandos de Turborepo. Desde el principio, tenemos cuatro comandos configurados en el package.json en la raíz del monorepo: build, dev, lint, y format usando Prettier.

Ahora veamos el archivo turbo.json en la raíz del proyecto:

{
  "$schema": "<https://turbo.build/schema.json>",
  "globalDependencies": ["**/.env.*local"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "lint": {},
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

Este archivo define los comandos build, lint y dev. Al ejecutar cualquiera de estos comandos, ya sea con npm o usando el comando global turbo, el comportamiento predeterminado es ejecutarlos en todos los workspaces que tengan ese comando configurado. Es decir, si ejecutas npm run build en la raíz del monorepo, compilará los workspaces docs y web, ya que son los únicos que en su package.json tienen un comando build configurado.

Aprovechando el Caché para Mayor Eficiencia

Un punto a destacar es que al ejecutar nuevamente el comando npm run build, puedes observar mensajes indicando el uso del caché:

Tasks:    2 successful, 2 total
Cached:    2 cached, 2 total
  Time:    162ms >>> FULL TURBO

Este mensaje indica que Turborepo identificó que no hubo cambios desde la última compilación y, por lo tanto, reutilizó la información en caché para acelerar el proceso. Esta optimización es particularmente valiosa en proyectos complejos, donde la reducción del tiempo de compilación y ejecución puede tener un impacto significativo en la productividad.

Flexibilidad de Ejecución con Filtros

Aunque la ejecución predeterminada de Turborepo aplica comandos a todos los workspaces relevantes, es común querer ejecutar un comando solo en un subconjunto específico de workspaces. Para atender esta necesidad, Turborepo ofrece la bandera --filter. Esta funcionalidad permite seleccionar qué workspaces serán el objetivo de la ejecución del comando, ofreciendo mayor flexibilidad y control sobre el flujo de trabajo.

La bandera --filter es especialmente útil cuando deseas aplicar un comando solo a un conjunto específico de workspacesque comparten características comunes. Por ejemplo, puedes filtrar workspaces en función de ciertas etiquetas, nombres u otros criterios definidos.

Explorando la Documentación

Turborepo ofrece una documentación extensa que detalla todos los recursos, opciones y mejores prácticas. La documentación oficial de Turborepo es una fuente valiosa de información para profundizar en tus conocimientos y descubrir formas avanzadas de optimizar tu flujo de trabajo.

Conclusión

En un entorno de desarrollo dinámico y en constante evolución, herramientas como Turborepo se destacan como soluciones vitales para optimizar la gestión de monorepos. Su enfoque innovador, que incluye el aprovechamiento del caché y la capacidad de filtrar workspaces, ofrece una ventaja competitiva al acelerar significativamente el ciclo de desarrollo.

Al adoptar Turborepo, los equipos de desarrollo pueden centrarse en crear código de alta calidad y funcionalidades, confiando en que la herramienta maneja los aspectos complejos de compilación, pruebas y ejecución. En resumen, Turborepo emerge como una herramienta poderosa y prometedora para equipos que trabajan con monorepos, ofreciendo eficiencia y velocidad para enfrentar los crecientes desafíos en el desarrollo de software.

💡
Las opiniones y comentarios emitidos en este artículo son propiedad única de su autor y no necesariamente representan el punto de vista de Listopro.

Listopro Community da la bienvenida a todas las razas, etnias, nacionalidades, credos, géneros, orientaciones, puntos de vista e ideologías, siempre y cuando promuevan la diversidad, la equidad, la inclusión y el crecimiento profesional de los profesionales en tecnología.