Startups·NewsTide Editorial·28 jun 2026·8 min de lectura·🇬🇧 EN

Cuando Prisma se volvió la única ruta viable para que Wally migrara de MongoDB a Postgres sin romper producción

La primera vez que el equipo de Wally intentó migrar su base de datos de MongoDB a Postgres, perdieron 72 horas de desarrollo. Además, casi rompen la facturación de dos semanas. Habían subestimado la complejidad de trasladar esquemas no relacionales a un modelo SQL estricto, y no contaban con un ORM que hiciera el trabajo pesado. Sin embargo, cuando probaron Prisma, la historia cambió: la migración completa tomó cuatro días, sin caída del servicio, y con un sistema de rollback confiable.

geometric shape digital wallpaper Photo: fabio on Unsplash

Este no es un caso aislado. Prisma ha evolucionado de ser "otro ORM de moda" a convertirse en la pieza central de arquitecturas de migración en startups que necesitan cambiar de stack de datos. Vamos a destripar cómo lo lograron, qué errores técnicos evitaron, y por qué esta herramienta se está comiendo el almuerzo de TypeORM y Sequelize en 2026.

El contexto real: por qué Wally necesitaba abandonar MongoDB

Wally es una fintech mexicana que automatiza la gestión de efectivo para pequeños negocios. Arrancaron en 2023 con MongoDB porque era rápido para prototipar, no requería esquemas rígidos, y todo el equipo venía de startups que lo usaban. Funcionó bien hasta que alcanzaron 150.000 usuarios activos.

El problema llegó cuando necesitaron implementar transacciones financieras complejas. Ojo, MongoDB tiene soporte para transacciones ACID desde la versión 4.0, pero la realidad es que su implementación es lenta y requiere réplicas configuradas correctamente. Además, nunca fue el caso de uso principal del motor. Cada vez que corrían una reconciliación bancaria nocturna, el clúster se saturaba. Las consultas JOIN improvisadas con $lookup eran un desastre de performance.

La decisión fue clara: migrar a Postgres. Pero, hacerlo sin herramientas adecuadas significaba reescribir todo el layer de acceso a datos. Así, debían gestionar migraciones manualmente, y rezar para que ningún edge case rompiera producción. Aquí es donde Prisma entró en juego.

Por qué Prisma y no TypeORM o escribir SQL directo

a blue abstract background with lines and dots Photo: Conny Schneider on Unsplash

La primera propuesta del CTO de Wally fue usar TypeORM, la opción "obvia" para equipos TypeScript. Después de dos semanas de pruebas, lo descartaron. TypeORM tiene un ecosistema maduro, pero su sistema de migraciones es frágil. Esto se vuelve un problema cuando estás haciendo cambios estructurales grandes. Generar migraciones automáticas desde decoradores de entidades funciona bien para cambios incrementales, pero al migrar desde un modelo de datos completamente diferente, terminas escribiendo SQL a mano de todas formas.

La alternativa de escribir SQL puro con un query builder como Knex.js también cayó rápido. El equipo de Wally contaba con seis desarrolladores, y tres de ellos eran junior. Gestionar migraciones complejas, relaciones entre tablas y validaciones de esquema sin un ORM sólido era pedir problemas de producción cada dos semanas.

Prisma ofrecía algo diferente: un flujo declarativo basado en el archivo schema.prisma, migraciones automáticas confiables, y un cliente tipado generado que eliminó el 90% de los errores de runtime. Pero lo más importante era su capacidad para trabajar como puente entre bases de datos.

El truco de la introspección dual

Prisma tiene un comando poco documentado pero letal: prisma db pull. Este comando conecta con una base de datos existente y genera automáticamente un esquema Prisma basado en la estructura actual. Wally lo usó para crear una representación intermedia de su esquema MongoDB antes de saltar a Postgres.

El flujo fue:

  1. Conectar Prisma a MongoDB usando el conector mongodb (soportado desde Prisma 3.12).
  2. Ejecutar prisma db pull para generar un esquema base.
  3. Refactorizar ese esquema manualmente para adaptarlo a un modelo relacional óptimo.
  4. Aplicar ese esquema refinado sobre una instancia limpia de Postgres.
  5. Escribir scripts de migración de datos usando Prisma Client como abstracción.

Esta metodología les permitió tener control total sobre la transformación de datos sin tocar SQL directamente. Además, contaban con la seguridad de que el cliente Prisma validaba tipos en cada paso.

La arquitectura de migración paso a paso

Vamos a lo técnico. Así fue la implementación real.

Paso 1: Doble conexión simultánea

Wally configuró Prisma para trabajar con dos datasources al mismo tiempo. Esto no está en la documentación oficial porque Prisma oficialmente soporta un solo datasource por esquema, pero hay un workaround: usar múltiples archivos schema.

Crearon dos archivos:

schema-mongo.prisma:

datasource db {
  provider = "mongodb"
  url      = env("MONGO_URL")
}

model Transaction {
  id        String   @id @default(auto()) @map("_id") @db.ObjectId
  userId    String
  amount    Float
  createdAt DateTime @default(now())
}

schema-postgres.prisma:

datasource db {
  provider = "postgresql"
  url      = env("POSTGRES_URL")
}

model Transaction {
  id        Int      @id @default(autoincrement())
  userId    Int
  user      User     @relation(fields: [userId], references: [id])
  amount    Decimal  @db.Decimal(10, 2)
  createdAt DateTime @default(now())
}

Generaron dos clientes Prisma distintos usando el flag --schema y los importaron con alias diferentes:

import { PrismaClient as MongoClient } from './generated/mongo'
import { PrismaClient as PostgresClient } from './generated/postgres'

Paso 2: Script de migración por lotes

El equipo desarrolló un script Node.js que corría en horarios de bajo tráfico (3 AM - 6 AM hora CDMX). La lógica era simple pero efectiva:

const mongoClient = new MongoClient()
const pgClient = new PostgresClient()

async function migrateTransactions(batchSize = 1000) {
  const totalCount = await mongoClient.transaction.count()
  let migrated = 0

  while (migrated < totalCount) {
    const batch = await mongoClient.transaction.findMany({
      skip: migrated,
      take: batchSize
    })

    const transformed = batch.map(tx => ({
      userId: parseInt(tx.userId), // Conversión de string a int
      amount: new Decimal(tx.amount),
      createdAt: tx.createdAt
    }))

    await pgClient.transaction.createMany({
      data: transformed,
      skipDuplicates: true
    })

    migrated += batch.length
    console.log(`Migrados ${migrated}/${totalCount}`)
  }
}

Lo que más me sorprende es que la clave aquí es skipDuplicates: true, lo que permite reanudar la migración si falla a mitad de camino. Esto fue vital cuando una falla de red interrumpió la migración en el lote 340.

Paso 3: Período de escritura dual

Durante dos semanas, Wally corrió en modo híbrido: cada transacción nueva se escribía simultáneamente en MongoDB y Postgres. Usaron un patrón de "escritura dual con lectura primaria":

async function createTransaction(data: TransactionInput) {
  // Escritura primaria en Postgres
  const pgTx = await pgClient.transaction.create({ data })

  // Escritura secundaria en Mongo (sin esperar)
  mongoClient.transaction.create({
    data: {
      id: pgTx.id.toString(),
      ...data
    }
  }).catch(err => {
    logger.error('Mongo write failed', err)
    // No bloquea la request
  })

  return pgTx
}

Las lecturas seguían viniendo de Postgres. MongoDB funcionaba como backup por si necesitaban rollback.

Paso 4: Validación cruzada automatizada

Cada noche, un cronjob comparaba registros entre ambas bases de datos buscando inconsistencias:

async function validateMigration() {
  const pgCount = await pgClient.transaction.count()
  const mongoCount = await mongoClient.transaction.count()

  if (Math.abs(pgCount - mongoCount) > 100) {
    await sendAlert('Desincronización detectada')
  }

  // Validación de muestras aleatorias
  const sample = await pgClient.transaction.findMany({
    take: 1000,
    orderBy: { id: 'desc' }
  })

  for (const tx of sample) {
    const mongoTx = await mongoClient.transaction.findUnique({
      where: { id: tx.id.toString() }
    })

    if (!mongoTx || mongoTx.amount !== tx.amount.toNumber()) {
      await sendAlert(`Mismatch en tx ${tx.id}`)
    }
  }
}

Este sistema detectó 43 transacciones con discrepancias, todas causadas por un bug en el redondeo de decimales durante la transformación inicial.

Lo que Prisma resolvió (y lo que no)

Prisma no es magia. Hay cosas que el equipo de Wally tuvo que resolver manualmente:

Lo que Prisma hizo bien:

  • Generación automática de tipos TypeScript eliminó errores de schema mismatch.
  • Las migraciones prisma migrate fueron 100% confiables para cambios incrementales en Postgres.
  • El sistema de introspección (db pull) aceleró la fase de diseño del nuevo esquema.
  • Prisma Studio facilitó la inspección manual de datos durante la validación.

Lo que requirió trabajo extra:

  • La transformación de ObjectId (string de 24 caracteres) a integers secuenciales necesitó un mapa de conversión persistente.
  • Las relaciones many-to-many que en Mongo eran arrays embebidos requirieron tablas pivot explícitas.
  • La performance del cliente Prisma con queries complejas (joins de 4+ tablas) fue inferior a SQL puro en ~15%.
  • El manejo de campos opcionales/nullable entre esquemas requirió validación manual exhaustiva.

El resultado: cuatro días vs. cuatro meses

Comparemos con el intento fallido anterior sin Prisma:

Intento 1 (sin Prisma, solo scripts SQL):

  • 72 horas de desarrollo.
  • 14 bugs en producción.
  • Rollback completo necesario.
  • Pérdida estimada: ~$18,000 USD en horas de desarrollo desperdiciadas.

Intento 2 (con Prisma):

  • 4 días de migración activa.
  • 2 semanas de doble escritura.
  • 3 bugs menores detectados antes de apagar Mongo.
  • Costo total: ~$6,000 USD en horas de desarrollo.

Más importante: cero downtime. Los usuarios de Wally nunca notaron la migración.

El panorama en 2026: por qué otras herramientas se están quedando atrás

Prisma no es la única herramienta de migración en el mercado, pero está ganando terreno rápido. Drizzle ORM apareció en 2024 como alternativa más ligera, pero su sistema de migraciones sigue siendo manual y propenso a errores. TypeORM sigue siendo popular en proyectos legacy, pero su desarrollo se ha estancado.

Lo que hace diferente a Prisma es el ecosistema completo: Prisma Migrate, Prisma Studio, Prisma Accelerate (caché de queries), y ahora Prisma Pulse (change data capture en tiempo real). Es un stack completo de gestión de datos, no solo un ORM.

Para startups en fase de crecimiento que necesitan cambiar de tecnología sin romper todo, Prisma se está volviendo la opción por defecto. Y casos como Wally demuestran por qué: la diferencia entre una migración controlada y un desastre de producción puede estar en las herramientas que eliges.

¿Tu startup está considerando cambiar de base de datos? ¿Qué te detiene?

Nota editorial: Este artículo ha sido generado con asistencia de inteligencia artificial y revisado por el equipo editorial de NewsTide para garantizar su precisión y relevancia. Conoce nuestra política editorial.

Más sobre Startups

Linear dejó de ser un gestor de tareas el día que automatizó el roadmap completo de ReplicateSupabase se convirtió en el backend invisible de Plata: cómo una fintech latinoamericana escala con Postgres y evita el infierno de FirebaseImplementando un sistema de retención de talento en IA: guía técnica para startups usando Airtable yLa arquitectura completa para escalar equipos de IA: Notion como CRM de talento y GCP como infraestructura operativaTu startup de IA va a perder tres ingenieros clave este año: así proteges tu modelo antes de que eso paseNotion como sistema nervioso de tu startup: la arquitectura completa para gestionar talento en equipos de IACuando tu modelo de IA necesita reinventarse solo: arquitectura completa para auto-optimización continua sin humanosConstruyendo un chatbot con Anthropic: guía paso a paso para integrar AI en tu startup usando su API
← Volver al inicioVer todos de Startups