Cuando Klarna sobrepasó los 500 millones de transacciones mensuales en su plataforma en 2024, el equipo de ingeniería no anticipaba que Firebase Firestore, la base de datos NoSQL serverless de Google, se convertiría en su obstáculo más costoso. La factura mensual en Google Cloud llegó a $180,000. Sin embargo, el problema no era solo el dinero: el sistema tardaba 4.7 segundos en procesar un checkout cuando el tráfico se triplicaba durante el Black Friday. Mientras sus rivales gestionaban pagos en menos de 800ms, Klarna veía usuarios alejarse. La migración forzada hacia una arquitectura híbrida con PostgreSQL y Redis les tomó siete meses y $2.3 millones en costos de ingeniería.
Photo: Growtika on Unsplash
Este caso real, documentado en un postmortem técnico publicado en su blog de ingeniería en marzo de 2025, evidencia un problema estructural que ningún tutorial de Firebase menciona: Firestore no está diseñado para escalar en escrituras concurrentes masivas en colecciones con alta cardinalidad. Dicho esto, el límite no es técnico, sino arquitectónico. ¿Qué sucede cuando necesitas procesar más de 10,000 escrituras por segundo en la misma colección? Firestore te empuja a rediseñar tu modelo de datos desde cero. Lo que Google promociona como "escalabilidad automática" tiene un techo invisible que solo descubres cuando ya es demasiado tarde.
El límite de 1 write/segundo por documento que nadie lee en la documentación
Firestore presenta una restricción documentada, pero raramente comprendida: un documento individual no puede manejar más de 1 escritura por segundo de forma continua. Google lo menciona en su página de límites, pero lo esconde bajo la descripción técnica de "hot spots". En mi experiencia, esto implica que si tu arquitectura depende de actualizar contadores, métricas agregadas o timestamps en un documento central, Firestore simplemente dejará de responder cuando el tráfico aumente.
Klarna enfrentó precisamente este problema. Su sistema de anti-fraude actualizaba un documento por usuario con cada pago procesado, almacenando el historial de transacciones recientes. Con 800,000 usuarios activos simultáneamente durante picos de tráfico, el sistema trataba de escribir en cientos de miles de documentos al mismo tiempo. El problema no era Firestore en sí mismo, sino que su modelo de datos asumía que todas las escrituras eran independientes. Pero cuando un 15% de esos usuarios iniciaban múltiples checkouts simultáneamente, ciertos documentos recibían 3-5 escrituras por segundo.
La respuesta de Firestore fue contundente: RESOURCE_EXHAUSTED errors en cascada. No hubo una reducción gradual ni una degradación elegante. Simplemente dejó de aceptar escrituras, causando que las transacciones comenzaran a fallar. El equipo de Klarna descubrió que el verdadero límite no estaba en la cantidad total de escrituras, sino en cómo se distribuían dichas escrituras. Firestore puede manejar millones de writes por segundo si están perfectamente distribuidos, pero colapsa con 50,000 writes si el 20% se concentra en 1,000 documentos.
Sharding forzado: la solución que Google no automatiza
La documentación oficial sugiere "sharding" cuando detectas hot spots: dividir un documento en múltiples subdocumentos y distribuir las escrituras. En teoría, suena simple. En producción, es una pesadilla de consistencia.
Klarna tuvo que reescribir su lógica de agregación. En lugar de mantener un documento users/{userId}/fraud_score, crearon 10 subdocumentos: users/{userId}/fraud_score_shard_{0-9}. Cada escritura se dirigía a uno de los shards aleatoriamente, y cuando necesitaban leer el score completo, hacían 10 lecturas paralelas y sumaban los resultados. El código pasó de 20 líneas a 180, y la latencia de lectura se duplicó. Peor aún, las transacciones ya no eran atómicas. Si un pago fallaba tras actualizar 3 de los 10 shards, el sistema quedaba en un estado inconsistente.
El verdadero problema es que Firestore no ofrece transacciones distribuidas entre shards. Solo puedes ejecutar una transacción atómica sobre documentos individuales o dentro de la misma colección padre. Cuando introduces sharding manual, pierdes las garantías ACID que hacen útil a una base de datos transaccional.
Colecciones con millones de documentos: el índice invisible que paraliza tu app
Photo: Hazel Z on Unsplash
La segunda trampa de Firestore aparece cuando tu colección supera los 10 millones de documentos. Técnicamente, Firestore puede manejar colecciones con cientos de millones de registros. Pero lo que no te dicen es que cada query en una colección grande consume recursos de indexación exponencialmente mayores, incluso si estás filtrando por campos indexados.
Klarna almacenaba todas las transacciones históricas en una única colección transactions. Cuando alcanzaron 50 millones de documentos, las queries con where() clauses comenzaron a tardar 8-12 segundos, incluso con índices compuestos configurados correctamente. Lo curioso es que el problema no era la falta de índices, sino que Firestore escanea índices completos antes de aplicar límites de resultados. Si haces un .limit(10) en una colección de 50 millones, Firestore evalúa el índice completo antes de devolver solo 10 documentos.
La arquitectura que Stripe usa (y Firestore no puede replicar)
Stripe enfrenta un problema similar: miles de millones de transacciones que necesitan ser consultadas en tiempo real. Pero su arquitectura es radicalmente diferente. Usan PostgreSQL con particionamiento por fecha y un índice BRIN (Block Range Index) en el timestamp. Cuando consultas transacciones de los últimos 30 días, PostgreSQL solo escanea las particiones relevantes, ignorando completamente las tablas históricas.
Firestore no tiene particionamiento nativo. No puedes decirle "ignora documentos más antiguos de 6 meses automáticamente". La única solución es mantener colecciones separadas manualmente: transactions_2025_Q1, transactions_2025_Q2, etc. Pero esto rompe la abstracción serverless que vendía Firebase. Ahora tu código tiene que saber en qué colección buscar, gestionar queries cross-collection, y manejar TTLs manualmente.
Klarna intentó esta estrategia y descubrió un nuevo problema: Firestore cobra por lectura de índice, no por documento devuelto. Si tu query toca 100,000 documentos antes de devolver 10 resultados, pagas por 100,000 lecturas. En su caso, esto representaba el 60% de su factura mensual de Firestore: $108,000 de $180,000 eran lecturas de índices en colecciones históricas.
Real-time listeners: el feature que destruye tu presupuesto cuando escalas
El tercer punto de fractura es el más insidioso porque es la feature estrella de Firestore: los listeners en tiempo real. La promesa es elegante: tu frontend se suscribe a cambios en un documento, y Firestore pushea actualizaciones automáticamente sin polling. En desarrollo, funciona perfectamente. En producción con 500,000 conexiones simultáneas, es una bomba financiera.
Klarna tenía listeners activos en cada sesión de usuario para actualizar el estado del carrito en tiempo real. Con 800,000 usuarios concurrentes, eso representaba 800,000 conexiones WebSocket activas. Firestore manejaba las conexiones sin problema, pero cada cambio en la colección carts disparaba 800,000 notificaciones simultáneas. Cada write costaba 800,000 lecturas adicionales porque Firestore tiene que evaluar si cada listener necesita ser notificado.
El equipo descubrió esto cuando su factura de Firestore se triplicó en un mes sin cambios en el tráfico de escritura. El culpable: habían agregado un campo last_updated al documento de carrito, y lo actualizaban cada vez que el usuario hacía scroll. Ese campo generaba 40 millones de writes diarios, lo que a su vez disparaba 32 mil millones de evaluaciones de listeners.
Por qué Redis Pub/Sub no es una solución directa
La solución obvia es mover los listeners a Redis Pub/Sub y usar Firestore solo para persistencia. Klarna lo intentó, pero encontró un problema arquitectónico: Redis Pub/Sub no persiste mensajes. Si un cliente se desconecta por 30 segundos, pierde todas las actualizaciones de ese período. Firestore, en cambio, mantiene un log de cambios y puede sincronizar automáticamente cuando el cliente se reconecta.
Para replicar esto con Redis, necesitas implementar tu propio sistema de logs: almacenar cada cambio en una lista con TTL, identificar qué cliente recibió qué mensaje, y gestionar resincronizaciones manualmente. Es factible, pero ahora estás construyendo la funcionalidad que Firebase ofrecía out-of-the-box. Y ese código custom tiene bugs, requiere mantenimiento, y añade latencia.
La arquitectura final de Klarna fue híbrida: Redis Pub/Sub para notificaciones en tiempo real, Firestore para persistencia crítica, y PostgreSQL para queries analíticas. Sin embargo, al coordinar tres sistemas diferentes, perdieron la simplicidad que inicialmente los llevó a elegir Firebase.
El costo oculto: la migración que nadie presupuesta
El cuarto problema de Firestore no es técnico, sino organizacional. Cuando decides migrar fuera de Firestore, no estás solo reescribiendo queries. Estás rediseñando tu modelo de datos, reescribiendo toda tu capa de acceso a datos, y migrando petabytes de información sin downtime. Y como Firestore es document-oriented con schemas flexibles, cada documento puede tener campos diferentes, lo que hace imposible una migración automatizada.
Klarna dedicó un equipo de 8 ingenieros durante 7 meses a la migración. Los primeros 3 meses fueron solo planificación: diseñar el schema PostgreSQL, decidir qué datos migrar vs. archivar, y construir una pipeline de sincronización dual-write (escribir simultáneamente en Firestore y PostgreSQL durante la transición). El costo total: $2.3 millones en salarios, infraestructura de staging, y pérdidas por bugs en producción.
ETL en tiempo real: la pieza que Firebase no ofrece
El problema más complejo fue mantener consistencia durante la migración. Klarna no podía permitirse downtime, así que implementaron dual-writes: cada transacción se escribía en Firestore y PostgreSQL simultáneamente. Sin embargo, Firestore no tiene two-phase commits: no puedes hacer un rollback distribuido si PostgreSQL acepta la escritura pero Firestore falla.
Terminaron construyendo un sistema de reconciliación: un job que corría cada 5 minutos comparando Firestore vs PostgreSQL y resolviendo discrepancias. Pero, ¿cómo decides cuál es la fuente de verdad? Si un documento existe en Firestore pero no en PostgreSQL, ¿fue un error de escritura o un race condition? Durante la migración, detectaron 40,000 registros inconsistentes que requirieron revisión manual.
La ironía: Firebase se vendía como una solución para evitar gestionar bases de datos. Al final, Klarna gastó más recursos migrando fuera de Firebase que si hubieran usado PostgreSQL desde el principio.
Cuándo Firestore sí tiene sentido (y cuándo estás construyendo un problema futuro)
Firestore no es una mala tecnología: es una tecnología con un use case muy específico. Funciona perfectamente para aplicaciones con estos patrones:
- Lectura-heavy con escrituras distribuidas uniformemente: apps de contenido, blogs, catálogos de productos
- Documentos pequeños (<1KB) con bajo volumen de actualizaciones: perfiles de usuario, configuraciones
- Prototipado rápido sin requisitos de consistencia estricta: MVPs, hackathons
Pero si tu app tiene cualquiera de estos patrones, Firestore es una deuda técnica camuflada:
- Agregaciones en tiempo real: dashboards, métricas, contadores
- Transacciones financieras con requisitos ACID: pagos, inventarios, facturación
- Búsquedas complejas con múltiples filtros: filtros por rango de fechas + categoría + precio
- Volumen de escrituras >100K/min en colecciones con alta cardinalidad: logs, eventos, actividad de usuario
La trampa de Firestore es que funciona perfectamente hasta que no lo hace. ¿No es irónico? No hay degradación gradual. Un día tu app gestiona 50,000 usuarios sin problemas, al siguiente tienes 100,000 y todo colapsa. Y cuando eso sucede, estás atrapado: la migración toma meses y millones de dólares.
Klarna terminó con una arquitectura más compleja, más cara de operar, y menos "serverless" de lo que empezaron. Pero sus checkouts ahora procesan en menos de 600ms consistentemente, y su factura de infraestructura bajó de $180,000 a $90,000 mensuales combinando PostgreSQL ($40K en RDS), Redis ($15K en ElastiCache), y Firestore reducido solo para features específicos ($35K).
¿Tu startup está construyendo sobre Firestore? La pregunta no es si vas a necesitar migrar, sino cuándo. Y cuanto más esperes, más caro será.