Serverless en producción: 12 lecciones aprendidas durante dos años
Dos años en producción demuestran que serverless no es solo ejecutar código sin servidores, sino operar un sistema distribuido con responsabilidades claras. Guía práctica sobre fiabilidad, costes, seguridad y operación.
Serverless en producción: 12 lecciones aprendidas durante dos años
¿Tu empresa está lista para IA? Descargá nuestro checklist gratuito →
Descargar checklistDos años de serverless: la lección principal
Después de dos años desarrollando, desplegando y operando sistemas serverless en producción, la conclusión más importante no es que serverless sea siempre la mejor opción. Es que serverless es un modelo de arquitectura y operación, no simplemente una forma de ejecutar funciones.
La palabra servidorless puede inducir a pensar que desaparece toda la infraestructura. En realidad, cambia quién la gestiona. El proveedor se encarga de_capacity planning, disponibilidad de la plataforma, parchesos y escalado; el equipo sigue siendo responsable del código, los datos, los permisos, las dependencias, la observabilidad, los costes y los modos de fallo.
En estos dos años aprendimos que un sistema serverless puede ser muy resiliente o(中)frágil depending on el diseño. La diferencia no suele estar en la tecnología de funciones, sino en decisiones como:
- Elegir workloads adecuados para ejecución stateless y短.
- Introducir colas y backpressure donde haya ráfagas de tráfico.
- Diseñar operaciones idempotentes desde el primer día.
- Medir cold starts, concurrencia y saturación por separado.
- Definir SLOs, alertas y runbooks antes de la primera incidencia.
- Tratar el coste como una propiedad arquitectónica.
- Automatizar infraestructura, pruebas, despliegues y reversiones.
Este artículos recoge doce lecciones prácticas, sus causas y las acciones concretas que recommendations para la siguiente vez.
> Nota: los ejemplos numéricos son ilustrativos y no representan benchmarks universales. Los límites, precios y capacidades cambian por proveedor y región, por lo que siempre hay que contrastarlos con la documentación vigente.
1. Serverless no significa ausencia de servidores
El error conceptual más común consiste en pensar que serverless elimina la complejidad. No la elimina: la distribuye.
Una función administrada por el proveedor sigue necesitando una plataforma de ejecución, red, sistema de archivos temporal, permisos, identidad, registros, métricas y mechanisms de failover. Además, cada invocación puede coincidir con otras funciones que compiten por los mismos recursos regional.
La división de responsabilidades queda así:
| Responsabilidad | Gestionada por el proveedor | Responsable el equipo de producto |
|---|---|---|
| Instancias,Availability y parchesos del runtime | Sí | No |
| Escalado de la función | Sí | Política, quotas y reservas |
| Código y dependencias | No | Sí |
| Permisos y acceso a datos | No | Sí |
| Esquemas y compatibilidad de eventos | No | Sí |
| Monitorización, alertas y respuesta | Parcialmente | Sí |
| Presupuesto y asignación de costes | Herramientas básicas | Sí |
Qué hicimos para corregir el enfoque
Creamos una checklist de diseño con cuatro capas:
- Capa de invocación: API Gateway, eventos, colas o tareas programadas.
- Capa de procesamiento: funciones, contenedores, workflows y dependencias.
- Capa de datos: bases de datos, almacenamiento, cachés y streams.
- Capa de operación: IAM, secretos, observabilidad, CI/CD, SLOs y runbooks.
Antes de aprobar un caso serverless, el equipo debe justificar las cuatro capas. Si una propuesta solo dice reduciremos infraestructura, todavía no es una propuesta arquitectónica.
2. No todos los workloads encajan igual
Serverless funciona especialmente bien con workloads que cumplen varias de estas características:
- Tráfico irregular o con picos.
- Tareas orientadas a eventos.
- Operaciones idempotentes y sin estado compartido.
- Tareas cortas o asíncronas.
- APIs con un volumen moderate y una latencia tolerante.
- Necesidad de aislar componentes y desplegarlos de forma independiente.
En cambio, suele ser más comfortable un servicio tradicional o基于 contenedores cuando:
- El consumo de CPU es sostenido y predecible.
- El proceso necesita RED? No: la运算 se mantiene activa durante horas.
- Hay computations intensive con una duración muy superior a los límites de la plataforma.
- El sistema exige una latencia mínima muy estricta y constante.
- Existe stateful complexity, como transacciones largas o coordinación distribuida compleja.
- El coste de una función serverless es claramente superior al de una capacidad预留.
Una regla de decisión simple
No comparamos función contra servidor. Comparamos el coste total de la unidad de trabajo: invocación, red, logs, almacenamiento, integración, operación y riesgo.
Un contenedor puede ser más barato para una cola constante de tareas largas. Una función puede ser más económica y flexible para millones de eventos isolated con picos. La mejor arquitectura puede incluso combinar ambos approaches: una API serverless que publica eventos y un worker de contenedores que procesa jobs более pesados.
3. El diseño asíncrono fue el cambio más importante
Los primeros sistemas fallaron en el punto más obvious:すべてのexternal dependencies se encadenaban dentro de una invocación síncrona.
Un flujo como este es frágil:
Cliente -> API Gateway -> Lambda -> Lambda -> Base de datos -> Lambda -> Servicio externo
Si la última llamada tarda o falla, se agota el timeout de toda la cadena. El cliente recibe errores y el trabajo que ya había begun puede quedar parcialmente processed.
El patrón que mejor funcionó fue convertir la operación en un flujo durable:
Cliente -> API Gateway -> Lambda de entrada -> SQS -> Lambda worker -> Base de datos
|
-> DLQ si persiste el fallo
El endpoint valida la solicitud, persiste una intención y devuelve 202 Accepted con un identificador de operación. El cliente puede consultar después el estado. Si el procesamiento tarda, no se pierde; se desacopla.
Un evento de ejemplo puede tener esta forma:
eventVersion: '1.0'
eventType: 'order.created'
eventId: 'evt_01JZ8Y7Q3M'
occurredAt: '2025-02-18T10:30:00Z'
orderId: 'ORD-10428'
customerId: 'CUS-9821'
items:
- sku: 'SKU-778'
quantity: 2
Tres reglas para adoption asíncrona
- Desacoplar no significa disparar y olvidar. El evento debe estar persistido, tener correlation ID y contar con un mecanismo de reintento.
- La API debe devolver un estado de aceptación verificable. Un
202sin un ID consultable traslada el problema al cliente. - Los contratos necesitan versionado. Añadir un campo suele ser compatible; cambiar el tipo o el significado no lo es. Usamos un
eventVersiony validamos el esquema al publicar y consumir.
4. Nunca prometimos exactly once; diseñamos idempotencia
Las colas, streams y mensajes de dominio pueden llegar más de una vez. En una arquitectura serverless, esto no es una excepción excepcional: es una posibilidad de diseño que debe asumirse.
Por ejemplo, una función actualiza el pedido, falla después de escribir y vuelve a recibir el mismo mensaje. Si no es idempotente, puede emitir una notificación duplicada, cobrar dos veces o aplicar dos descuentos.
La solución fue combinar una clave de idempotencia con una escritura condicional:
from botocore.exceptions import ConditionalCheckFailedException
def process_order(event, context):
order_id = event['orderId']
item = {
'orderId': order_id,
'status': 'processed',
'processedAt': event['occurredAt']
}
try:
orders_table.put_item(
Item=item,
ConditionExpression='attribute_not_exists(orderId)'
)
return {'status': 'processed'}
except ConditionalCheckFailedException:
logger.info('order_already_processed', extra={'order_id': order_id})
return {'status': 'already_processed'}
Este fragmento es ilustrativo, pero el patrón es aplicable: la segunda entrega detecta que el estado ya existe y finaliza sin repetir efectos.
Lecciones concretas
- La clave de idempotencia debe ser estable y tuya, no un timestamp generado en cada retry.
- Duplicados técnicos y duplicados de negocio requieren respuestas diferentes.
- Una dead-letter queue no es un lugar donde ignorar errores. Necesita所有者, alerta, procedimiento de análisis y replay controlado.
- FIFO puede ayudar a ordenar o deduplicar durante una ventana limitada, pero no sustituye la idempotencia de negocio.
- Cuando un flujo modifica una base de datos y publica un evento, usamos transactional outbox o un stream de cambios para evitar dual writes inconsistentes.
5. Cold starts: medimos antes de optimize
El cold start se convirtió en una obsessionPerhaps excessive. La debate solía centrarse en anecdotes y no en datos del propio sistema.
Medimos por separado:
Init Duration: inicialización del runtime, módulos e extensiones.Duration: tiempo de ejecución de la función.- Latencia percibida por el cliente.
- Distribución p50, p95 y p99.
- Errores y throttles asociados.
- Traffic por versión, concurrencia y franja horaria.
Un p99 alto puede deberse a un cold start, una dependencia lenta, una cola de ejecución o un pico de demanda. Sin esta separación, cualquier optimization se basa en suposiciones.
Cuándo sí optimizamos
Aplicamos estas medidas solo cuando el SLO lo exigía:
- Reducir dependencias y paquetes innecesarios.
- Cargar módulos pesados de forma perezosa.
- Reutilizar conexiones fuera del handler cuando el entorno lo permite.
- Evitar inicializar clientes costosos en cada invocación.
- Usar concurrencia reservada para endpoints con latencia estricta y tráfico estable.
- Evaluar funciones de arranque rápido del proveedor cuando encajen con el lenguaje.
La concurrencia reservada no es una cura universal. Tiene coste y puede aumentar el throttling de otras funciones si se reserva demasiado. Antes de activarla, comprobamos que el presupuesto lo permite y que el problema persists después de optimize código.
6. La concurrencia necesita backpressure explícito
La escalabilidad serverless es notable, pero no infinita. Por ejemplo, la documentación de AWS Lambda establece un límite de concurrencia por defecto de 10.000 ejecuciones simultáneas por región. También documenta una ventana de escalado de hasta 1.000 entornos de ejecución cada 10 segundos por función y región.
El límite exacto puede cambiar y algunas organizaciones negocian quotas, pero la lección permanece: el pico de demanda puede superar la capacidad disponible.
¿Querés un diagnóstico personalizado? Completá el checklist gratuito →
Descargar checklistCuando una流行lar de tráfico alcanzó esa frontera, vimos throttles y latencia. La respuesta no fue simply increase the memory. Fue:
- Colocar una cola entre el productor y el consumidor.
- Limitar la concurrencia de la función crítica.
- Medir edad del mensaje, no solo número de mensajes.
- Escalar consumers según profondeur de cola.
- Definir un límite de admission para proteger las dependencias.
def handler(event, context):
order_id = event['orderId']
# La condición debe件数 con una reserva real, no ser decorativa.
if reserved_concurrency_is_exhausted():
queue.send_message(
MessageBody=event['body'],
MessageAttributes={
'orderId': {'StringValue': order_id}
}
)
return {'status': 'queued'}
return process_order(event, context)
La concurrencia reservada protege una función frente a la starvation, pero no genera magically capacidad adicional. Si se reserva una parte importante del pool, el resto puede ser throttled. Por eso el balance entre isolation y capacidad total debe managearse con datos.
7. Diseñamos cada fallo, no solo el camino feliz
En un sistema distribuido, la pregunta no es si una dependencia fallará, sino qué ocurrirá cuando lo haga.
Los patrones que incorporamos fueron:
- Timeouts explícitos en clientes HTTP y bases de datos.
- Retries con backoff exponencial y jitter para errores transitorios.
- Un límite máximo de intentos para evitar storms de reintento.
- Dead-letter queues para mensajes que agotan los reintentos.
- Circuit breakers cuando una dependencia está claramente degradada.
- Reintentos distintos para
429,5xxtimeouts y errores no recuperables. - Checks de idempotencia antes de repetir un efecto.
- Alertas sobre la DLQ, no solo sobre la latencia.
Un error de validación, por ejemplo, no debería consumir los mismos reintentos que una caída temporal de red. Clasificar errores reduce ruido y evita convertir un fallo pequeño en una incidencia mayor.
La outskirts que nos costó más
El mayor riesgo no fue perder un mensaje aislado, sino que una caída prolongada enviara el mismo evento miles de veces y saturara bases de datos y APIs externas. El backoff, el jitter, los límites y el control de concurrencia son essential para evitar retry storms.
8. La observabilidad tuvo que ser end-to-end
La monitorización de funciones isolated rápidamente se queda corta. Para saber por qué un cliente recibió un error, había que correlacionar:
- El request ID del API Gateway.
- El correlation ID propagado al evento.
- La invocación de cada función.
- El mensaje de cola.
- La operación en la base de datos.
- La llamada a servicios externos.
El modelo que adoptamos
Logs estructurados: un evento por línea, JSON, con timestamp, request ID, tenant, operation ID y resultado. Nunca incluimos tokens, secretos ni datos personales innecesarios.
logger.info(
'order_processed',
extra={
'order_id': order_id,
'operation_id': operation_id,
'duration_ms': elapsed_ms
}
)
Métricas RED y de negocio: además de rate, errors y duration, medíamos pedidos creados, aceptados, processed y rechazados. Un servicio puede tener un 200 rate perfecto y estar perdiendo pedidos por una regla de negocio incorrecta.
Tracing distribuido: adoptamos OpenTelemetry y propagación del contexto W3C. Así una transacción puede atravesar funciones, colas y servicios sin perder su relación temporal.
SLOs accionables: definimos, por ejemplo:
- 99,9 % de solicitudes aceptadas en menos de 500 ms.
- 99 % de eventos processed en menos de cinco minutos.
- Cero pérdida conocida de eventos durante 30 días.
- Tiempo máximo de recuperación de 30 minutos.
Las alertas se basan en síntomas y error budgets, no en cada fluctuación de CPU. Cada alerta crítica enlaza a un runbook con diagnóstico, responsables y criteria de escalado.
9. La seguridad no puede añadirse al final
Serverless reduce gestión de servidores, pero concentra riesgos en permisos, datos y código de terceros. En los primeros despliegues subestimamos la superficie de configuración.
Controles que incorporamos
- IAM de mínimo privilegio: cada función tiene un rol específico. Eliminamos acciones
sobre recursos. - Autenticación en el borde: validamos tokens antes de invocar la lógica de negocio.
- Validación de entrada: todo payload externo es no confiable, incluidos mensajes de una cola con control de acceso.
- Gestión de secretos: los secretos se recuperan de un almacén seguro, se rotan y se cachean de forma controlada. Las variables de entorno no son un sistema de rotación.
- Cifrado y aislamiento: usamos cifrado en tránsito y reposo, claves gestionadas y políticas de bucket separadas por tenant cuando aplica.
- Dependencias: activamos SCA, SBOM y actualización regular de runtimes y paquetes.
- Protección frente a abuso: rate limits, límites de payload y cuotas por cliente para controlar coste y denegación de servicio.
- VPC solo cuando aporta valor: no pusimos cada función en una VPC por costumbre. Un entorno VPC puede añadir latencia de inicialización y complexity de red.
La revisión de IAM fue especialmente útil. Un rol con permisos amplios para resolver rápidamente una integración terminó、近 access a más datos del necesario. El principio de menor privilegio-obliga a designing integrations as isolated capabilities.
10. El coste depende de la arquitectura, no solo del proveedor
El error más común es comparar únicamente el precio de cómputo por segundo. Una función serverless genera además requests, logs, red, API Gateway, almacenamiento, colas,NAT gateway y other integrated services.
Para entender el modelo, tomamos como referencia la tarifa publicada de AWS Lambda en us-east-1 para x86: 0,20 USD por millón de requests y aproximadamente 0,0000166667 USD por GB-segundo. Supongamos un millón de invocaciones de 100 ms con 256 MB:
GB-s por invocación = 0,256 GB x 0,1 s = 0,0256
Coste de cómputo = 1.000.000 x 0,0256 x 0,0000166667 = 0,4267 USD
Coste de requests = 1.000.000 x 0,20 USD / 1.000.000 = 0,20 USD
Subtotal orientativo = 0,6267 USD
El subtotal no incluye free tiers, logs, API Gateway, transferencia, colas ni almacenamiento. Con un segundo de ejecución, el mismo volumen elevaría el cómputo a unos 4,27 USD, mientras que el coste de requests seguiría siendo 0,20 USD.
Palancas de optimización
- Reducir invocaciones innecesarias mediante batch processing cuando el contrato lo permita.
- Ajustar memoria con datos reales; más memoria puede reducir duración y latencia.
- Evitar logging masivo o datos de payload completos.
- Aplicar filtros y sampling a logs sin perder trazas críticas.
- Usar caché cuando reduce trabajo repetitivo y respeta consistencia.
- Establecer budgets y alertas de coste por servicio, environment y tenant.
- Evaluar el coste total cuando una función procesa millones de eventos pequeños.
La conclusión fue clara: un diseño orientado a muchos miles de micro-eventos puede ser más caro que un consumidor de contenedor, aunque cada función individual parezca barata.
11. Automatización, testing y entregas progresivas
La manually console no apareció en nuestra checklist. La infraestructura se definijo con Terraform, AWS CDK o SAM, según el contexto, y cada cambio pasó por code review.
El pipeline checks básico fue:
format -> lint -> unit tests -> security scan -> build
-> deploy dev -> integration tests -> deploy canary
-> smoke tests -> progressive rollout
Estrategia de pruebas
Aplicamos una pirámide adapted al modelo serverless:
- Unitarias: lógica de negocio, validaciones, cálculos y manejo de errores sin red.
- De contrato: compatibilidad entre productor, consumidor y esquema del evento.
- De integración: función, cola, base de datos, IAM, secretos y lifecycle completo en un environment isolated.
- End-to-end: los recorridos críticos desde un consumidor real o simulador until el estado final observable.
- De carga: picos, concurrencia, throttling, comportamiento de queues y límites de dependencies.
No esperamos a producción para descubrir que un mensaje antiguo no cumple el nuevo schema. En cada release, validamos eventos antiguos y nuevos durante la transición.
Despliegues seguros
- Versionado de funciones y artefactos inmutables.
- Aliases con pesos para canary releases.
- Feature flags para separar código de activation.
- Rollback probado, no solamente documentado.
- Migraciones de datos backward-compatible.
- Separación entre despliegues de infraestructura y cambios destructivos.
Un rollback que nunca se ha probado es solo una intención. Durante el primer año, simulamos fallos de despliegue y verificamos cuánto tardaba el equipo en recuperar el servicio.
12. Redujimos el lock-in, pero no sobreingenierizamos
La portabilidad total no es realista cuando serverless utiliza strengths proprietarios del proveedor. Aun así,nous(diminishing) reduzimos el acoplamiento en las fronteras más importantes:
- Contratos de API con OpenAPI.
- Eventos con esquemas versionados y, cuando tiene sentido, CloudEvents.
- Lógica de negocio separada de adaptadores de infraestructura.
- Acceso a datos mediante interfaces propias.
- IaC revisable y portable where practical.
- Evitar featuresPlatform-only in components designed for exit.
No|Pages construimos un framework universal para evitar un posible futuro cambio. Esa abstracción tiene su propio coste. El equilibrio correcto es mantener la lógica crítica portable y aceptar las ventajas de la plataforma en las capas de ejecución e integración.
Un plan de adopción de 90 días
Para compartir estas lecciones con un equipo que comienza,ESTA es la hoja de ruta que recomendamos.
Días 1-15: descubrir y medir
- Seleccionar un proceso no crítico con tráfico irregular.
- Documentar flujo, datos, dependencias yOwner.
- Medir latencia, errores, throughput, costes y baseline de carga.
- Definir SLO inicial y budget de coste.
- Identificar efectos idempotentes y datos sensibles.
Días 16-30: construir el piloto
- Crear IaC desde el primer commit.
- Implementar validaciones, tracing y logs estructurados.
- Añadir tests de contrato e integración.
- Crear DLQ, alertas y runbook.
- Comparar resultados con la línea base.
Días 31-60: endurecer
- Ejecutar pruebas de carga y picos.
- Introducir backpressure y límites de concurrencia.
- Probar retries, duplicados y replay de DLQ.
- Revisar IAM, secrets, cifrado y dependencias.
- Medir cold starts y unit economics con datos reales.
Días 61-90: liberar progresivamente
- Desplegar un canary con un porcentaje pequeño de traffic.
- Comparar SLOs, costes y métricas de negocio.
- Practicar rollback y recuperación ante una dependencia caída.
- Documentar límites conocidos y Responsibilities.
- Decidir si se amplía, se adjusta o se detiene el piloto.
El success criteria no debe ser que el piloto functioned once. Debe ser que el equipo puede operarlo, diagnosticarlo, escalarlo y modificarlo con confianza.
Los antipatrones que dejamos de repetir
- Poner todas las dependencias en una sola invocación síncrona.
- Activar retries sin backoff, jitter ni límite.
- Confundir una DLQ con un archivo de errores ignorados.
- Suponer exactly-once delivery.
- Añadir observabilidad únicamente después del incidente.
- Usar IAM amplio para acelerar la integración.
- Medir solo el average y ocultar latencias p95 y p99.
- Optimizar cold starts sin conocer su impacto real.
- Crear una función por cada línea de negocio.
- Modificar infraestructura manualmente desde la consola.
- Ignorar NAT, logs, API Gateway y storage al calcular coste.
- Lanzar al 100 % sin canary ni rollback probado.
Fuentes y lecturas recomendadas
- Límites y cuotas de AWS Lambda
- Precios de AWS Lambda
- Configuración de concurrencia en AWS Lambda
- Entrega at-least-once en colas estándar de Amazon SQS
- Serverless Lens de AWS Well-Architected Framework
Conclusión: serverless funciona cuando el diseño respeta la realidad
Los dos años de experiencia nos Spaniaron una idea simple: serverless no es magic. Es un tradeoff entre management de infraestructura, flexibilidad, aislamiento, coste y responsabilidad operativa.
Funciona muy bien cuando se elige para processing short, event-driven y bursty; cuando se diseñan colas, idempotencia y backpressure; y cuando observabilidad, seguridad y automatización forman parte del producto desde el inicio.
El siguiente paso es actionable: elige un flujo medium-sized, documenta sus dependencias y crea una línea base de latencia, errores y coste. Después construye un piloto pequeño con SLO, DLQ, canary y rollback. Con esos datos, la decisión de adoptar serverless dejará de depender de una convicción y se basará en evidencia.
En Tanok Tech, ayudamos a equipos a diseñar, medir y operar arquitecturas serverless que pueden sostenerse en producción, no solo demostrarse en un workshop.
¿Listo para dar el próximo paso? Evaluá tu empresa con nuestro checklist gratuito →
Descargar checklistPublicaciones relacionadas
- AI & ML◈
Las mejores herramientas de IA para desarrolladores en 2026: las que debes tener
Las mejores herramientas de IA para desarrolladores en 2026: las que debes tener
5 nov 2026
- Data≈
Tarjetas perforadas: de los telares a la computación
Tarjetas perforadas: de los telares a la computación
4 nov 2026
- APIs⟶
MCP: el nuevo estándar para conectar la IA con tus herramientas
MCP: el nuevo estándar para conectar la IA con tus herramientas
3 nov 2026