Skip to main content
backend4 de enero de 202613 min de lectura

Cloud Functions vs backend tradicional: cuándo tiene sentido serverless

Una comparación real entre funciones serverless y backends tradicionales — cubriendo costo, latencia, experiencia de desarrollo y cuándo gana cada enfoque.

serverlesscloud-functionsbackend
Cloud Functions vs backend tradicional: cuándo tiene sentido serverless

Cada proyecto greenfield que inicio me obliga a tomar la misma decisión: ¿levanto un servidor tradicional o escribo cloud functions? Después de lanzar sistemas en producción en ambos lados — Firebase Functions para workflows basados en eventos, AWS Lambda para endpoints de API y servidores Express/Fastify para todo lo demás — tengo una opinión bastante matizada sobre cuándo realmente gana cada enfoque. La respuesta, como era de esperar, es "depende," pero los detalles de qué depende valen la pena explorar en profundidad.

Definiendo los dos enfoques

Antes de comparar, permíteme ser preciso sobre qué quiero decir con cada uno.

Backend tradicional se refiere a un proceso de servidor de larga ejecución — una aplicación Express de Node.js, un servidor FastAPI de Python, un servidor HTTP de Go — desplegado en una VM, contenedor o plataforma de cómputo gestionado. El proceso se inicia, permanece en ejecución y maneja solicitudes a medida que llegan. Los destinos de despliegue incluyen EC2, Cloud Run, ECS, Railway, Fly.io o un VPS básico.

Cloud functions (funciones serverless) son manejadores de funciones individuales que se ejecutan en respuesta a eventos. La plataforma gestiona la infraestructura de cómputo por completo. Tu código se ejecuta, retorna una respuesta, y el entorno de ejecución puede o no persistir para la siguiente invocación. Los proveedores incluyen AWS Lambda, Google Cloud Functions, Firebase Functions, Azure Functions y Cloudflare Workers.

La distinción no es sobre el lenguaje o framework — es sobre el modelo de ejecución. Un backend tradicional es dueño del ciclo de vida de su proceso. Una cloud function no.

La realidad del cold start

Los cold starts son la limitación serverless más discutida, y la realidad en 2026 es más matizada de lo que sugiere el discurso.

Qué sucede realmente durante un cold start

Cuando una cloud function no ha sido invocada recientemente (o cuando las invocaciones concurrentes exceden las instancias warm disponibles), la plataforma debe:

  1. Aprovisionar un nuevo entorno de ejecución (microVM o contenedor)
  2. Descargar y extraer tu paquete de despliegue
  3. Inicializar el runtime (Node.js, Python, etc.)
  4. Ejecutar tu código de inicialización (imports de módulos, configuración de SDKs, conexiones de BD)
  5. Ejecutar el manejador de función real

Los pasos 1-3 son overhead de la plataforma. El paso 4 es donde las decisiones de tu código importan enormemente.

Números de cold start en la práctica

Aquí están los tiempos de cold start que he medido en diferentes plataformas y configuraciones en proyectos reales:

Plataforma Runtime Tamaño del bundle Cold Start
AWS Lambda Node.js 20 5 MB 250-400ms
AWS Lambda Node.js 20 50 MB 800-1200ms
AWS Lambda Python 3.12 10 MB 400-600ms
Firebase Functions v2 Node.js 20 15 MB 600-1000ms
Cloudflare Workers JavaScript 1 MB 0-5ms
Google Cloud Functions v2 Node.js 20 10 MB 300-500ms

Cloudflare Workers son la excepción porque usan V8 isolates en lugar de contenedores, lo cual elimina la penalización de inicio de contenedor por completo. El trade-off es un entorno de ejecución más restringido.

Mitigando cold starts

Hay varias estrategias que genuinamente ayudan:

Concurrencia provisionada (Lambda): Mantiene N instancias warm en todo momento. Cuesta $0.0000041667 por GB-segundo de capacidad provisionada. Para una función de 256MB, eso es aproximadamente $3.20/mes por instancia warm. Vale la pena para endpoints sensibles a la latencia.

Instancias mínimas (Cloud Run, Firebase Functions v2): Mismo concepto, diferente nombre. Cloud Run cobra por instancias inactivas a una tarifa reducida.

Reducción del tamaño del bundle: Esto tiene la mejor relación impacto-esfuerzo. Tree-shaking, excluir dependencias de desarrollo y usar clientes de SDK más ligeros reducen drásticamente el tiempo de inicialización.

// Bad: imports the entire AWS SDK (adds 40MB+ to bundle)
import AWS from 'aws-sdk';

// Good: imports only the S3 client (adds ~3MB)
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';

Inicialización lazy: No establezcas conexiones a base de datos ni cargues recursos pesados en el ámbito del módulo. Inicialízalos en la primera invocación y reutiliza en invocaciones warm.

import { Pool } from 'pg';

let pool: Pool | null = null;

function getPool(): Pool {
  if (!pool) {
    pool = new Pool({
      connectionString: process.env.DATABASE_URL,
      max: 1, // Single connection per Lambda instance
    });
  }
  return pool;
}

export async function handler(event: APIGatewayEvent) {
  const db = getPool();
  const result = await db.query('SELECT * FROM users WHERE id = $1', [event.pathParameters?.id]);
  return { statusCode: 200, body: JSON.stringify(result.rows[0]) };
}

Comparación de costos a diferentes escalas

El costo es donde la conversación se pone interesante, porque la economía se invierte conforme el tráfico escala.

Tráfico bajo (1,000 - 50,000 solicitudes/día)

A esta escala, serverless es casi siempre más barato. Muchas startups y proyectos secundarios caen aquí.

Costo de Lambda para 30,000 solicitudes/día, 200ms de duración promedio, 256MB de memoria:

  • Cargos por solicitud: 900K solicitudes/mes x $0.20/millón = $0.18
  • Cargos de cómputo: 900K x 0.2s x 0.25GB x $0.0000166667 = $0.75
  • Total: ~$1/mes

Servidor tradicional equivalente (t4g.small en AWS):

  • Bajo demanda: $12.26/mes
  • Con Savings Plan de 1 año: ~$8/mes

Con tráfico bajo, estás pagando por un servidor siempre activo que está 95% inactivo.

Tráfico medio (100,000 - 1,000,000 solicitudes/día)

Esta es la zona de cruce donde la comparación se acerca.

Costo de Lambda para 500,000 solicitudes/día, 200ms promedio, 256MB:

  • Cargos por solicitud: 15M/mes x $0.20/millón = $3.00
  • Cargos de cómputo: 15M x 0.2s x 0.25GB x $0.0000166667 = $12.50
  • Total: ~$15.50/mes

t4g.medium (2 vCPU, 4GB RAM):

  • Bajo demanda: $24.53/mes
  • Con Savings Plan: ~$16/mes

Los costos son comparables, pero el servidor tradicional maneja este tráfico con margen significativo. Si tu tráfico es estable, el servidor es ligeramente más barato. Si tu tráfico es variable (concentrado en días laborales, ráfagas por eventos), la facturación por invocación de Lambda significa que no pagas por períodos tranquilos.

Tráfico alto (5,000,000+ solicitudes/día)

A esta escala, serverless es casi siempre más caro para cargas de trabajo sostenidas.

Costo de Lambda para 5,000,000 solicitudes/día, 200ms promedio, 512MB:

  • Cargos por solicitud: 150M/mes x $0.20/millón = $30
  • Cargos de cómputo: 150M x 0.2s x 0.5GB x $0.0000166667 = $250
  • Total: ~$280/mes

c6g.large (2 vCPU, 4GB RAM) con auto-scaling group:

  • 2 instancias con Savings Plan: ~$60/mes
  • Con balanceador de carga: ~$16/mes
  • Total: ~$76/mes

El enfoque tradicional es 3.7x más barato a esta escala, y la brecha se amplía conforme el tráfico aumenta.

Costos ocultos a considerar

La facturación de Lambda no es la imagen completa. Suma API Gateway ($1-3.50 por millón de solicitudes), CloudWatch Logs ($0.50/GB ingerido) y X-Ray tracing si lo usas. Del lado tradicional, suma costos de balanceador de carga, monitoreo y el tiempo de operaciones para gestionar instancias y despliegues.

Diferencias en experiencia de desarrollo

DX serverless

Ventajas:

  • Sin infraestructura que gestionar (sin parches, sin configuración de escalado)
  • Despliegue por función significa menor radio de explosión
  • Desarrollo local con emuladores (Firebase Emulator Suite, SAM CLI, Serverless Framework offline)
  • Observabilidad integrada a través del logging y tracing de la plataforma

Puntos de dolor:

  • El desarrollo local nunca replica perfectamente el comportamiento de producción
  • Depurar llamadas distribuidas función-a-función es más difícil que recorrer paso a paso un monolito
  • Los límites de tamaño de despliegue (Lambda: 250MB descomprimido) restringen las opciones de dependencias
  • La configuración de IAM y permisos es verbosa y fácil de equivocar
  • Los cold starts hacen los ciclos de iteración de desarrollo más lentos al testear contra funciones desplegadas

DX de backend tradicional

Ventajas:

  • El entorno de desarrollo local es el entorno de producción (mismo proceso, mismo estado)
  • La depuración es directa — adjuntar un debugger, poner breakpoints, recorrer paso a paso
  • Sin límites de tamaño de despliegue
  • Los patrones de middleware, hooks de ciclo de vida de request y manejo global de errores son naturales
  • Soporte de WebSocket, procesos de larga ejecución y trabajos en segundo plano funcionan sin servicios extra

Puntos de dolor:

  • Eres dueño del ciclo de vida de la infraestructura (actualizaciones, escalado, health checks)
  • Los despliegues requieren orquestación (rolling updates, períodos de gracia de health check)
  • El escalado requiere configuración (auto-scaling groups, orquestación de contenedores)
  • El monitoreo y logging requieren configuración explícita

La brecha de frameworks se está cerrando

Frameworks como SST (Serverless Stack) y Architect han mejorado dramáticamente la DX serverless. SST en particular proporciona un modo de desarrollo "Live Lambda" donde tu código ejecutándose localmente maneja invocaciones de Lambda en tiempo real, eliminando el ciclo de desplegar-testear por completo.

Del lado tradicional, plataformas como Railway, Fly.io y Render han simplificado el despliegue a git push. La carga operativa de ejecutar un backend tradicional ha disminuido significativamente.

Cuándo gana serverless

Cargas de trabajo basadas en eventos

Si tu código se ejecuta en respuesta a eventos — subidas de archivos, cambios en base de datos, mensajes de cola, tareas programadas — serverless es el encaje natural. No estás manteniendo un loop de polling ni un proceso listener. La plataforma activa tu código exactamente cuando se necesita.

// Firebase Functions: triggered on Firestore document creation
import { onDocumentCreated } from 'firebase-functions/v2/firestore';

export const onOrderCreated = onDocumentCreated('orders/{orderId}', async (event) => {
  const order = event.data?.data();
  if (!order) return;

  // Send confirmation email
  await sendEmail(order.customerEmail, {
    template: 'order-confirmation',
    data: { orderId: event.params.orderId, items: order.items },
  });

  // Update inventory
  await updateInventory(order.items);

  // Notify restaurant
  await sendPushNotification(order.restaurantId, {
    title: 'New Order',
    body: `Order #${event.params.orderId} received`,
  });
});

Tráfico esporádico o impredecible

Una herramienta interna que recibe 50 solicitudes el lunes por la mañana y 2 solicitudes el resto de la semana. Un receptor de webhooks que procesa eventos de una API de terceros. Un generador de reportes programado que se ejecuta por 30 segundos una vez al día. Estas cargas de trabajo tienen costo casi cero en serverless y desperdician dinero en infraestructura siempre activa.

Endpoints de API con tráfico bajo a medio

Para la API de un MVP de startup sirviendo unos miles de solicitudes por día, serverless proporciona un backend funcional con mínima sobrecarga operativa y costo casi cero. Puedes enfocarte completamente en la lógica de negocio.

Procesamiento de datos en paralelo

¿Necesitas procesar 10,000 imágenes, transcodificar 500 videos o ejecutar analytics en un conjunto de datos grande? Distribuye en miles de invocaciones concurrentes de Lambda, procesa en paralelo y paga solo por el tiempo de cómputo usado. Lograr el mismo paralelismo con servidores tradicionales requiere aprovisionar (y pagar por) esa capacidad pico.

Cuándo ganan los backends tradicionales

Conexiones persistentes

Conexiones WebSocket, Server-Sent Events, streams gRPC y long-polling requieren una conexión persistente entre cliente y servidor. Las funciones Lambda tienen un límite de ejecución de 15 minutos y no están diseñadas para conexiones de larga duración.

Puedes resolver esto con API Gateway WebSocket APIs o AWS IoT Core, pero la complejidad y el costo a menudo exceden un simple servidor WebSocket en Cloud Run o una plataforma de contenedores.

Pipelines de request complejos

Cuando una solicitud pasa por autenticación, rate limiting, validación de entrada, lógica de negocio, operaciones de base de datos, actualizaciones de caché, emisión de eventos y formateo de respuesta — el patrón de pipeline de middleware de Express/Fastify/Koa es ergonómicamente superior al patrón de handler plano de cloud functions.

// Traditional: clean middleware pipeline
app.use(cors());
app.use(helmet());
app.use(rateLimiter);
app.use(authenticate);
app.use('/api/v1', apiRouter);
app.use(errorHandler);

Replicar esto en Lambda requiere un framework como Middy (que agrega peso y complejidad) o duplicar código de configuración entre funciones.

Operaciones con estado

Si tu aplicación mantiene estado en memoria — un caché, un pool de conexiones, un contador de rate limiter, datos de sesión — un servidor tradicional maneja esto naturalmente. Las funciones Lambda pueden reutilizar estado entre invocaciones warm, pero no puedes depender de ello. Cualquier estado que deba persistir necesita un servicio externo (Redis, DynamoDB), lo cual agrega latencia y costo.

Requisitos de latencia estrictos

Para APIs donde cada milisegundo importa — pujas en tiempo real, servidores de juegos, transacciones financieras — la latencia impredecible introducida por cold starts es inaceptable, incluso con concurrencia provisionada. Un proceso de servidor bien afinado con pooling de conexiones y cachés warm entrega tiempos de respuesta consistentes sub-10ms que serverless no puede garantizar.

El enfoque híbrido

En la práctica, la mayoría de los sistemas de producción que construyo terminan siendo híbridos. La API principal se ejecuta en un backend tradicional (Cloud Run o una plataforma de contenedores), mientras las cargas de trabajo auxiliares se ejecutan en cloud functions.

Una arquitectura híbrida práctica

Client
  │
  ├─→ API Gateway / Load Balancer
  │     └─→ Cloud Run (main API)
  │           ├─→ PostgreSQL (Cloud SQL)
  │           ├─→ Redis (Memorystore)
  │           └─→ Pub/Sub (event bus)
  │
  └─→ Cloud Functions (event handlers)
        ├─→ Image processing (on upload)
        ├─→ Email sending (on order creation)
        ├─→ Analytics aggregation (scheduled)
        └─→ Webhook processing (HTTP trigger)

La API principal maneja tráfico sincrónico de request-response con conexiones persistentes a la base de datos, caché en memoria y latencia consistente. Las cloud functions manejan cargas de trabajo asíncronas basadas en eventos donde los cold starts no importan porque el usuario no está esperando una respuesta.

Estrategia de migración: tradicional a serverless

Si tienes un backend tradicional existente y quieres adoptar serverless selectivamente:

  1. Identifica operaciones basadas en eventos actualmente ejecutándose como trabajos en segundo plano, tareas cron o consumidores de colas. Estas son las más fáciles de migrar.
  2. Extrae endpoints independientes que no comparten estado con otros endpoints. Endpoints de health check, receptores de webhooks y APIs utilitarias son buenos candidatos.
  3. Mantén la API principal tradicional a menos que tengas una razón convincente para descomponerla. La sobrecarga operativa de gestionar 50 funciones Lambda a menudo excede la de gestionar un contenedor.
  4. Usa un bus de eventos (Pub/Sub, EventBridge, SQS) como la capa de integración entre tu API tradicional y las funciones serverless. La API publica eventos; las funciones se suscriben y reaccionan.

Estrategia de migración: serverless a tradicional

Ir en la dirección opuesta a veces es necesario cuando una API serverless supera su arquitectura:

  1. No reescribas todo a la vez. Comienza consolidando las funciones de mayor tráfico en un solo servidor API.
  2. Mantén las funciones basadas en eventos como funciones. Solo migra manejadores de request-response.
  3. Usa las mismas rutas de API. Apunta tu API Gateway/balanceador de carga al nuevo servidor para las rutas migradas y continúa enrutando a Lambda para el resto.
  4. Migra las conexiones de base de datos primero. La mayor ganancia de rendimiento es pasar de configuración de conexión por invocación a pooling de conexiones persistente.

Notas específicas por plataforma

Firebase Functions (v2)

Firebase Functions v2 se ejecuta sobre Cloud Run por debajo, lo cual le da ventajas significativas sobre v1: concurrencia configurable (múltiples solicitudes por instancia), instancias mínimas, timeouts más largos (hasta 60 minutos) y asignaciones de memoria mayores. Si estás en funciones v1 de Firebase, la actualización a v2 vale el esfuerzo de migración.

El Firebase Emulator Suite proporciona la mejor experiencia de desarrollo local en el espacio serverless. Emula Firestore, Auth, Storage y Functions en un solo entorno local, con hot-reload en cambios de código.

AWS Lambda

Lambda tiene el ecosistema de integración más profundo — triggers desde virtualmente cualquier servicio de AWS, layers para código compartido y SnapStart para cargas de trabajo Java. La función de URL de función elimina la necesidad de API Gateway para casos de uso simples, ahorrando $3.50 por millón de solicitudes.

Lambda@Edge y CloudFront Functions son poderosos para manipulación de request/response a nivel de CDN, pero la experiencia de desarrollo y depuración es significativamente peor que Lambda estándar.

Cloud Run

Cloud Run difumina la línea entre serverless y tradicional. Ejecuta contenedores (cualquier lenguaje, cualquier framework), escala a cero y soporta concurrencia (múltiples solicitudes por instancia). Es serverless en modelo de facturación pero tradicional en modelo de ejecución. Para equipos que quieren la economía serverless sin reescribir su código como manejadores de funciones, Cloud Run es a menudo la respuesta correcta.

Tomando la decisión

Aquí está el framework de decisión que uso:

  1. ¿La carga de trabajo está basada en eventos y es asíncrona? Serverless.
  2. ¿El tráfico es bajo e impredecible? Serverless.
  3. ¿Necesitas conexiones persistentes o estado en memoria? Tradicional.
  4. ¿La latencia baja consistente es crítica? Tradicional.
  5. ¿El equipo es pequeño y adverso a las operaciones? Serverless (o Cloud Run).
  6. ¿El tráfico es alto y estable? Tradicional (más barato).
  7. ¿Es una mezcla de lo anterior? Híbrido.

La peor decisión es tratar esto como una elección de todo o nada. Usa la herramienta correcta para cada carga de trabajo, conéctalas a través de interfaces bien definidas, y obtendrás los beneficios de ambas sin comprometerte completamente con ninguna.

DU

Danil Ulmashev

Full Stack Developer

Interesado en trabajar juntos?