wandres.dev
NIVEL DIOS: SÍNTESIS · el toolchain unificado

Arquitectar el build de un producto a escala

Dominas las piezas por separado: pnpm, Vite, el bundler, el empaquetado, el monorepo, la caché remota, el splitting y el deploy. Arquitectar es componerlas en un sistema donde cada decisión restringe y habilita a la siguiente. Esta lección traza el mapa completo de decisiones, de la raíz del repositorio al edge, y enseña a leerlo como una cadena acoplada y no como un inventario de herramientas.

⏱ 20 min

Llegas aquí sabiendo cada pieza por separado: pnpm y su store, Vite y sus dos naturalezas, el bundler Rolldown sobre Oxc, el empaquetado de librerías, el monorepo con Turborepo, la caché remota, el code splitting y el deploy al edge. Arquitectar no es conocer más piezas: es componerlas en un sistema donde cada decisión restringe y habilita a la siguiente. Un build de producto a escala no es una lista de herramientas, sino una cadena de compromisos que empieza en la raíz del repositorio y termina en un CDN a milisegundos del usuario. Esta lección traza ese mapa entero y te enseña a recorrerlo como cadena, no como inventario.

🎯 Al terminar esta lección sabrás
  • Ver el build como una cadena de decisiones acopladas, no como una caja de herramientas.
  • Ordenar las cinco capas —repo, instalación, dev, bundle, deploy— y sus dependencias.
  • Reconocer qué decisión de cada capa restringe las opciones de la siguiente.
  • Salir con un mapa mental que puedas aplicar a cualquier producto real.

Cinco capas, una sola cadena

La tentación al empezar un proyecto es elegir herramientas por separado —“uso pnpm, Vite y Vercel”— como si fueran platos de un bufé independientes. No lo son. Cada elección estrecha el abanico de la siguiente, y las decisiones tempranas son las que más pesan porque las heredan todas las capas de arriba. Arquitectar bien es reconocer ese acoplamiento y decidir en el orden correcto: de la raíz hacia el edge, no al revés.

El camino de src a producción atraviesa cinco capas, y cada una responde una pregunta distinta:

  • Estructura del repo — ¿un solo paquete o muchos? Monorepo con workspaces o polyrepo. Esta decisión gobierna todo lo demás.
  • Instalación y resolución — ¿qué package manager y qué node-linker? pnpm con su store enlazado, catalogs para unificar versiones, frozen-lockfile en CI.
  • Desarrollo — ¿qué dev server y qué motor? Vite sobre Rolldown, HMR, la Environment API cuando hay SSR.
  • Empaquetado — ¿qué produce el build? tree shaking, code splitting por ruta, minificación, hashing de assets, budgets.
  • Orquestación y deploy — ¿cómo evitas repetir trabajo y dónde corre? Turborepo con caché remota, CI con provenance, deploy al edge.
flowchart TD
repo[Estructura del repo: monorepo o polyrepo] --> inst[Instalacion: pnpm store y catalogs]
inst --> dev[Desarrollo: Vite sobre Rolldown]
dev --> bundle[Empaquetado: tree shaking splitting budgets]
bundle --> orq[Orquestacion: Turborepo y cache remota]
orq --> edge[Deploy al edge]
repo -.restringe.-> orq
style repo fill:#cba6f7,color:#11111b
style edge fill:#a6e3a1,color:#11111b
style orq fill:#89b4fa,color:#11111b

Fíjate en la arista punteada: la estructura del repo no solo condiciona la instalación, sino que salta hasta la orquestación. Elegir monorepo obliga a tener una capa de caché por tareas; elegir polyrepo la vuelve innecesaria pero fragmenta las versiones. Ninguna capa se decide sola.

Las decisiones que restringen

Cuatro decisiones tempranas fijan el carácter de todo el sistema. Tómalas mal y pasarás el resto del proyecto compensando; tómalas bien y las capas de arriba caen por su propio peso.

🌳

Monorepo o polyrepo

Monorepo unifica versiones con catalogs, comparte código sin publicar y habilita el affected. Su precio es una capa de orquestación obligatoria: sin caché por tareas, un monorepo grande es insoportable.

📦

App o librería

Una app se empaqueta para un runtime concreto y agrupa dependencias; una librería se publica con exports, tipos .d.ts y externals. Confundirlas produce bundles que reempaquetan React o librerías que no hacen tree shaking.

✂️

Cómo trazas los cortes

El code splitting por ruta y el import() dinámico definen qué descarga el usuario primero. Los cortes se piensan en la arquitectura de rutas, no se parchean al final con manualChunks.

🗄️

Qué cacheas y dónde

La caché local acelera tu máquina; la remota comparte el trabajo entre personas y CI. Definir bien los inputs y outputs de cada tarea es lo que separa una caché fiable de una que se envenena.

💡
Decide desde la restricción, no desde la moda

El orden correcto para arquitectar es empezar por la restricción más dura y dejar que propague. Si el producto tendrá varios equipos tocando código compartido, la restricción es “compartir sin publicar” y de ahí sale el monorepo, y del monorepo sale la caché remota, y de la caché remota sale la disciplina de inputs y outputs. Si en cambio publicas una librería para terceros, la restricción es “consumo estable” y de ahí salen exports, el dual package y el versionado semver. La herramienta de moda no es una restricción; es una respuesta a una restricción que quizá no tienes.

El presupuesto como brújula

A escala, la métrica que importa deja de ser “compila rápido” y pasa a ser doble: no compilar lo que no cambió y no enviar al navegador lo que no se usa. Son las dos caras de la misma disciplina de frugalidad, y las dos se declaran, no se esperan.

Del lado del build, la frugalidad es la caché por tareas. Turborepo hashea las entradas de cada tarea —archivos, dependencias, variables— y, si nada cambió, replica la salida en milisegundos en vez de recompilar. La condición es declarar con precisión qué entra y qué sale.

// turbo.json: la caja de una tarea, declarada para que el hash sea fiable
{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "inputs": ["src/**", "package.json", "tsconfig.json"],
      "outputs": ["dist/**"]
    }
  }
}

Del lado del cliente, la frugalidad es el presupuesto de bundle. Un budget convierte una intención vaga —“que cargue rápido”— en un límite que el CI hace cumplir, de modo que una dependencia pesada colada sin querer rompe el build en vez de degradar en silencio la experiencia del usuario.

// Un limite duro: el CI falla si el chunk de entrada crece de mas
export default {
  build: {
    chunkSizeWarningLimit: 200,
    rollupOptions: { output: { manualChunks: { vendor: ['react', 'react-dom'] } } }
  }
}
⚠️
La caché mal aislada miente en verde

El fallo más caro de un monorepo no es que la caché no acelere, sino que acelere de más: reutiliza una salida que ya no corresponde a las entradas reales. Ocurre cuando una tarea lee algo que no declaraste en sus inputs —una variable de entorno, un archivo de configuración en la raíz, la hora del sistema— y entonces dos estados distintos del repo colisionan en el mismo hash. El resultado es un build que pasa en verde y despliega bits equivocados. Por eso la fidelidad de inputs y outputs no es burocracia: es la condición para que la caché sea correcta y no solo rápida.

Del commit al edge

La última capa cierra la cadena: el mismo trabajo hasheado que evita recompilar en tu máquina viaja a CI por la caché remota, y de CI sale un artefacto firmado que se despliega al edge. Cada capa hereda las garantías de la anterior, y cada capa tiene su modo de fallo característico.

Capa Decisión clave Herramienta 2026 Modo de fallo
Repo monorepo o polyrepo pnpm workspaces versiones divergentes
Instalacion store y lockfile pnpm y catalogs build no reproducible
Desarrollo dev server y motor Vite sobre Rolldown dev y prod difieren
Empaquetado splitting y budgets Rolldown y Oxc bundle inflado
Deploy cache remota y edge Turborepo y CDN trabajo repetido en CI

Leer esta tabla de arriba abajo es leer la cadena entera. Un problema en producción casi nunca vive donde se manifiesta: un bundle inflado suele nacer de una librería mal empaquetada tres capas más abajo, y un build irreproducible en CI suele nacer de un lockfile no congelado en la capa de instalación. Diagnosticar a escala es saber recorrer la cadena hacia atrás.

Arquitectar es ordenar restricciones, no coleccionar herramientas

La diferencia entre quien “hace que compile” y quien arquitecta el build de un producto a escala no está en cuántas herramientas conoce, sino en si ve el sistema como una lista o como una cadena. La lista invita a decidir cada capa por su cuenta, eligiendo en cada una lo más rápido o lo más popular, y produce sistemas donde las junturas se abren: un monorepo sin caché que tarda veinte minutos, una app que reempaqueta React porque alguien la trató como librería, un dev que diverge de producción porque nadie unificó el motor. La cadena, en cambio, se recorre en un solo sentido: identificas la restricción más dura del producto —compartir código entre equipos, publicar para terceros, servir a milisegundos del usuario en todo el mundo— y dejas que propague hacia arriba, capa por capa, cada decisión estrechando y habilitando la siguiente. La estructura del repo fija si necesitas orquestación; la orquestación fija la disciplina de inputs y outputs; esa disciplina fija si tu caché es correcta; y la caché correcta es lo que convierte veinte minutos en veinte segundos. Ninguna de esas piezas gana sola; ganan porque encajan. Y por eso el trabajo más valioso al diseñar un build no es configurar la herramienta de moda, sino escribir en una frase cuál es la restricción que gobierna el producto, porque de esa frase se deduce, casi mecánicamente, todo el resto del mapa. Quien sabe nombrar la restricción arquitecta; quien solo sabe nombrar herramientas, ensambla.

⚔️ Dibuja la cadena de tu producto
  1. Escribe en una sola frase la restricción más dura de un proyecto real tuyo: compartir código, publicar para terceros, o latencia global.
  2. Deriva de esa frase la decisión de las cinco capas y anota, en cada una, qué opción descarta la restricción.
  3. Abre tu turbo.json o equivalente y verifica que los inputs de la tarea build incluyen todo lo que la tarea lee de verdad.
  4. Mide tu bundle de entrada y ponle un budget en la config; provoca un fallo metiendo una dependencia pesada y confirma que el CI la detiene.
  5. Traza hacia atrás un problema real de producción que hayas sufrido y localiza en qué capa nació, aunque se manifestara en otra.