Optimización de Serverless Functions en Vercel: El Patrón que Reduce Cold Starts un 70%

Optimización de Serverless Functions en Vercel: El Patrón que Reduce Cold Starts un 70%

Programming· 7 min read

El 90% de los Desarrolladores Optimizan el Peso del Código, No el Arranque en Frío

Vuestra función de Vercel tarda 1,8 segundos en responder. El código que escribís tarda 50msg. El resto es ejecución del runtime y carga de dependencias.

El problema real no es la velocidad de vuestro código. Es que estáis pagando el coste de importación antes de que vuestra lógica arranque.

La mayoría de desarrolladores asumen que el límite de 60 segundos en Vercel Serverless Functions significa que sus funciones deben ser rápidas pero pueden ser pesadas. Cargaréis 15MB de dependencias, un cliente de base de datos enorme, validación de schemas gigantes. Total, el timeout es generoso.

Esto es un error arquitectónico que os cuesta más de lo que creéis.

El Ciclo de Vida Que Nadie Mide

Cuando una Serverless Function de Vercel recibe una petición, el ciclo es: init → handler → response. Durante init, Node.js ejecuta todos los imports de nivel superior. Cada require() o import top-level se ejecuta antes de que vuestro handler reciba el primer byte.

En una función simple esto parece irrelevante. Pero:

Lo que creéis: import desde el top level → el código es más limpio → mejor rendimiento

La realidad: import desde el top level → se ejecuta en frío → consume vuestros 60 segundos antes de empezar

Una función Express típica con middleware puede cargar 2MB+ antes de ejecutar una línea de vuestro handler. body-parser, cors, helmet, jsonwebtoken. Cada uno de estos importa a su vez docenas de módulos. El resultado: vuestro "cold start" puede ser de 500ms a 2s en producción, y consumís una porción significativa del budget sin ejecutar lógica de negocio.

En Vercel, el plan Hobby tiene un timeout de 60 segundos. El plan Pro sube a 900 segundos. Pero ninguna de estas cifras importa si gastáis el 30% del budget en inicializar módulos que no usáis en el 70% de las peticiones.

La Falacia del Éxito Táctico

Aquí viene el patrón que veo constantemente: desarrolladores que optimizan la lógica interna de sus funciones (algoritmos, loops, procesos asíncronos) mientras ignoran el coste de setup.

He aquí el desglose real de una función típica:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

La lógica de negocio ocupa el 8,5% del tiempo de ejecución total.

Si optimizáis esa lógica un 50%, ahorráis 60ms. Si elimináis las dependencias innecesarias, ahorráis 890ms. La diferencia es de un orden de magnitud.

La mayoría de artículos sobre optimización de Serverless Functions os dirán que uséis Redis para cachear resultados, que optimicéis vuestras queries, que implementéis streaming de respuestas. Todo esto está bien. Pero son micro-optimizaciones dentro de un problema arquitectónico mayor.

El Patrón de Inicialización Diferida en 5 Fases

Esta es la estrategia que realmente reduce cold starts en Vercel. No es una sola técnica — es un sistema de 5 fases que aborda el problema desde múltiples ángulos.

1. Perfila y Aísla el Coste Real

Primero, desplegad una función minimal que únicamente loguea process memory usage y timestamp antes y después de cada import. Comparad el resultado con vuestra función de producción.

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Ejecutad esta función desde el dashboard de Vercel y observad los logs. El output os mostrará exactamente dónde va el tiempo. En la mayoría de funciones que analizo, el paso de imports supera el 60% del tiempo total de init.

2. Audita el Árbol de Dependencias

Usad un bundle analyzer para identificar qué ocupa espacio. En el ecosistema Node.js, herramientas como madge para graficar dependencias y esbuild para bundling os permiten ver exactamente qué está inflando vuestras funciones.

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Cuando ejecutáis el análisis, buscad módulos que:

  • Pesan más de 100KB
  • Se importan pero se usan en menos del 20% de los casos
  • Contienen datos de locale que nunca usáis (date-fns es particularmente problemática aquí)

El objetivo no es eliminar funcionalidad. Es diferir la carga hasta que sea necesaria.

3. Convierte Imports Top-Level a Imports Dinámicos

Aquí está la transformación clave. Estructurad los handlers para usar import() dentro de la función, no desde el nivel superior.

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

El coste: la primera petición en frío paga el precio de importación. Pero las peticiones warm subsiguientes usan los módulos ya cacheados en memoria. En patrones de tráfico real donde una instancia recibe múltiples peticiones, el coste amortizado es significativamente menor.

4. Evalúa la Migración a Edge Functions

Para paths que son críticos en latencia — autenticación, routing, manipulación de headers — considerad migrar a Vercel Edge Functions. Estas corren en V8 isolates con cold starts cercanos a cero.

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

La restricción: Edge Functions no tienen acceso a APIs de Node.js (no fs, no crypto nativo, APIs de runtime limitadas). Pero para validación, routing y transformación de requests, ofrecen rendimiento que Serverless Functions no pueden igualar dentro del mismo budget.

El patrón arquitectónico correcto: Edge Function para el fast path → Serverless Function para heavy compute.

5. Implementa Cacheo In-Function para Resultados Repetidos

Dentro de la ventana de 60 segundos, cachead resultados de operaciones costosas en variables scoped al warm instance. Reconexiones de base de datos, validaciones de tokens, transformaciones de datos repetidos — todo esto puede evitar trabajo redundante.

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Este patrón funciona porque Vercel reutiliza instancias warm. Una función que recibe 100 peticiones por instancia puede cachear conexiones y resultados sin pagar el coste de setup en cada request.

El Framework en Acción

Implementad las 5 fases en secuencia:

  1. Medid: Desplegad el benchmark, observad los logs de Vercel, identificad dónde va el tiempo
  2. Auditad: Ejecutad bundle analysis, identificad módulos >100KB que no se usan frecuentemente
  3. Diferid: Convertid imports top-level a dynamic import() dentro de handlers
  4. Migrad: Identificad paths latency-críticos que pueden correr en Edge, separad la responsabilidad
  5. Cachead: Implementad cacheo in-function para operaciones repetidas dentro del warm instance

El resultado esperado: cold starts de 1.400ms+ reducidos a 200-400ms, más espacio para lógica de negocio dentro del límite de 60 segundos.

Por Qué Esto Funciona Cuando Otros Enfoques Fracasan

La mayoría de guías de optimización de Vercel se centran en la velocidad del código. Os dicen que optimizéis queries, que uséis índices, que implementéis Redis. Todo esto tiene valor. Pero son micro-optimizaciones dentro de un problema de arquitectura.

El Patrón de Inicialización Diferida ataca la causa raíz: el momento en que se ejecuta el código importa tanto como el código mismo. Un import en top level consume vuestro budget antes de que empiece la lógica útil. Un import diferido paga el precio solo cuando la funcionalidad es necesaria.

Esto es particularmente relevante para aplicaciones con patrones de tráfico asimétricos: muchas peticiones que no necesitan toda la funcionalidad (por ejemplo, health checks, validación de headers) y pocas peticiones que requieren el código pesado completo.

El Siguiente Paso

No-optimizáis lo que no medís. Empezad con la función de benchmark. Desplegadla, ejecutadla varias veces, observad los logs. El output numérico os dirá exactamente dónde está el problema y cuánto espacio hay para mejora.

Una vez que tengáis los datos, la transformación es mecánica: identificáis los módulos más pesados, los convertís a imports dinámicos, auditáis qué puede vivir en Edge. Cada iteración reduce el cold start. Cada reduction de cold start os deja más budget para la lógica que realmente importa.

El límite de 60 segundos no es vuestro enemigo. Vuestra configuración de dependencias sí lo es.

Artículos relacionados

---

¿Quieres recibir contenido como este cada semana? Suscríbete a mi newsletter

Brian Mena

Brian Mena

Software engineer building profitable digital products: SaaS, directories and AI agents. All from scratch, all in production.

LinkedIn