wandres.dev
SMART PLACEMENT Y LÍMITES · CPU y subrequests

El tamaño del bundle

Tu Worker no se despliega como lo escribes: se empaqueta, se comprime y se sube, y ese paquete tiene un techo. Vemos el límite del script comprimido —3 MB en Free, 10 MB en Paid, 64 MB antes de comprimir—, por qué un bundle grande también frena el arranque del isolate, cómo medir el tamaño real con Wrangler, y las tácticas para adelgazarlo: sacar los datos del bundle, elegir dependencias ligeras y partir el código en trozos que se cargan cuando hacen falta.

⏱ 15 min

Hay un límite que no se manifiesta en tiempo de ejecución sino en el momento de desplegar, y por eso sorprende: el tamaño del script. Tu Worker no viaja a la red tal como lo escribes, sino empaquetado en un único bundle, comprimido con gzip, y ese paquete no puede pasar de cierto peso. El número parece generoso hasta que metes una librería pesada o incrustas un fichero de datos, y de pronto el despliegue se niega. Entender qué entra en ese paquete, y qué no debería, es una habilidad de arquitectura tanto como de empaquetado.

🎯 Al terminar esta lección sabrás
  • Conocer el límite del script comprimido por plan y el tope antes de comprimir.
  • Entender por qué un bundle grande también penaliza el tiempo de arranque del isolate.
  • Medir el tamaño real, comprimido, de tu Worker antes de desplegar.
  • Adelgazar el bundle sacando datos, eligiendo dependencias ligeras y partiendo el código.

El límite del script comprimido

El tope se mide sobre el bundle ya comprimido con gzip, no sobre tu código fuente. En el plan Free son 3 MB comprimidos; en Paid, 10 MB. Hay además un límite antes de comprimir, de 64 MB en ambos planes, pero el que sueles tocar es el de gzip, porque el JavaScript comprime muy bien y el fuente rara vez se acerca a los 64.

Que el límite sea sobre el comprimido tiene una consecuencia práctica: no puedes estimarlo mirando el tamaño de tu carpeta src. Un árbol de dependencias de varios megabytes puede caber holgado tras la compresión, o un solo fichero binario incrustado puede dispararlo pese a parecer pequeño en disco. Hay que medir el número real, no intuirlo.

# empaqueta sin desplegar y muestra el tamano comprimido
npx wrangler deploy --outdir bundled/ --dry-run
# la salida se parece a esto: fijate en la cifra tras gzip
Total Upload: 259.61 KiB / gzip: 47.23 KiB

La cifra que importa es la que va después de gzip. Esa es la que se compara contra tu límite, y la que conviene vigilar en integración continua para que un despliegue no se rompa por sorpresa el día que alguien añade una dependencia gorda.

Por qué el tamaño frena el arranque

El peso del bundle no es solo una cuestión de si el despliegue pasa o no: también moldea la latencia de la primera petición. Antes de atender nada, el isolate tiene que parsear y ejecutar el ámbito global de tu script —todo el código de nivel superior, fuera de los manejadores— y para eso tiene un presupuesto de un segundo.

Un bundle grande tarda más en parsearse, y si además tu código de nivel superior hace trabajo caro al arrancar —construir tablas enormes, compilar expresiones regulares, inicializar librerías— consumes ese segundo antes de haber respondido a nadie. La regla es mantener el ámbito global liviano: declara, no ejecutes; y deja el trabajo caro para dentro del manejador o, mejor, para la primera vez que de verdad se necesita.

💡
Los source maps no cuentan contra el bundle

Subir el source map de tu Worker no engorda el paquete ni penaliza el arranque: se procesa fuera de banda, solo para remapear los stack traces de tus errores al código original. Puedes subirlos sin miedo —hasta 15 MB comprimidos— y ganar depuración legible en producción sin coste de tamaño ni de arranque.

Adelgazar el bundle

La primera pregunta ante un bundle gordo no es cómo comprimir más, sino qué no tendría que estar ahí dentro. Los datos son el sospechoso habitual: ficheros de configuración, activos estáticos, blobs binarios, tablas de traducción. Nada de eso pertenece al script.

📦

Saca los datos del bundle

Configuración, activos y binarios viven mejor en KV, R2, D1 o en Static Assets. El Worker los pide en ejecución en lugar de cargarlos empaquetados en su propio peso.

🪶

Elige dependencias ligeras

Antes de importar una librería enorme por una sola función, mira si una Web API estándar —como WebCrypto o URL— ya te la da. La dependencia más barata es la que no añades.

✂️

Parte el código

Con imports dinámicos, el código de una ruta poco frecuente solo se carga cuando esa ruta se pide. El árbol se divide en trozos y el arranque no paga por lo que casi nunca corre.

🔗

Divide en varios Workers

Si dos partes de tu app crecen sin parar, sepáralas en Workers distintos conectados por Service Bindings. Cada uno tiene su propio presupuesto de tamaño.

El code splitting merece un matiz. El empaquetador elimina por sí solo el código muerto si tus dependencias son módulos ESM que permiten tree-shaking; una librería que solo se exporta como CommonJS a menudo entra entera aunque uses una función. Por eso, a igualdad de utilidad, la dependencia moderna y modular pesa menos en tu bundle final que la veterana monolítica.

// en vez de importar el generador de PDF arriba y cargarlo siempre,
// lo traes solo cuando la ruta que lo usa se pide de verdad
export default {
  async fetch(request, env): Promise<Response> {
    const url = new URL(request.url);
    if (url.pathname === "/informe.pdf") {
      const { generarPdf } = await import("./pdf.ts");
      return generarPdf(env);
    }
    return new Response("ok");
  },
};
flowchart LR
SRC[Tu codigo y dependencias] --> BUND[Empaquetado y tree shaking]
BUND --> GZIP[Comprimido con gzip]
GZIP --> CHK{Cabe en el limite del plan}
CHK -- si --> DEPLOY[Se despliega]
CHK -- no --> FIX[Saca datos y parte el codigo]
FIX --> BUND
DATA[Configuracion activos y binarios] --> STORE[KV R2 D1 o Static Assets]
style GZIP fill:#89b4fa,color:#11111b
style DEPLOY fill:#a6e3a1,color:#11111b
style FIX fill:#fab387,color:#11111b
style STORE fill:#cba6f7,color:#11111b
El tamaño del bundle mide la disciplina de tu frontera de despliegue

Es fácil ver el límite de tamaño como un fastidio de empaquetado, pero conviene leerlo como lo que de verdad es: un incentivo estructural que te obliga a separar lo que es código de lo que son datos, y lo que se ejecuta siempre de lo que se ejecuta a veces. En un servidor tradicional esa distinción se difumina, porque el disco es enorme y el proceso arranca una vez y vive días: da igual que la aplicación pese cientos de megabytes o que cargue tablas gigantes al iniciar, porque ese coste se amortiza en un arranque único y remoto. El edge invierte esa economía. Tu Worker no arranca una vez, sino miles de veces en miles de ubicaciones, y cada arranque paga el peso del bundle en tiempo de parseo dentro de un presupuesto de un segundo. De golpe, el tamaño deja de ser una cifra vanidosa y se convierte en un impuesto que cobras a cada isolate que nace. Y la forma de no pagarlo es una higiene que resulta ser buena arquitectura por otros motivos: los datos fuera del código, en el almacén que les corresponde, porque son datos y no lógica; las dependencias justas, porque cada una es superficie que mantener y peso que arrastrar; el código de las rutas raras cargado solo cuando se pisan, porque el arranque no debería pagar por lo excepcional. Interiorizar el límite de tamaño es entender que en el edge el bundle no es un artefacto de compilación que se olvida tras subirlo, sino una unidad viva que se replica y se ejecuta sin descanso, y que cada byte que le añades lo multiplicas por toda la red. La disciplina que impone es, en el fondo, la misma que distingue un buen diseño de módulos: que cada cosa esté donde le toca, y que solo esté presente lo que de verdad hace falta.

⚔️ Mide y adelgaza
  1. Ejecuta wrangler deploy --dry-run --outdir sobre un Worker real y anota la cifra tras gzip; compárala con el límite de tu plan.
  2. Localiza en tu proyecto un fichero de datos o un activo incrustado en el bundle y muévelo a KV, R2 o Static Assets.
  3. Convierte un import estático de una librería usada en una sola ruta en un import dinámico y comprueba el efecto en el tamaño y el arranque.
  4. Elige una dependencia pesada y busca si una Web API estándar cubre lo que usabas de ella; estima el peso que ahorrarías.
  5. Explica por qué el límite se mide sobre el comprimido y por qué no puedes deducir si cabrás mirando el tamaño de tu carpeta de fuentes.