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.
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
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:
- Conectar Prisma a MongoDB usando el conector
mongodb(soportado desde Prisma 3.12). - Ejecutar
prisma db pullpara generar un esquema base. - Refactorizar ese esquema manualmente para adaptarlo a un modelo relacional óptimo.
- Aplicar ese esquema refinado sobre una instancia limpia de Postgres.
- 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 migratefueron 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?