Saltar al contenido principal

Cómo Validamos una Migración de Datos Financieros hasta el 0.002% de Precisión

Cómo logramos menos de $2,000 de varianza sobre el conjunto completo de datos de ingresos — una tasa de precisión del 0.002% — usando modelos duales paralelos, validación jerárquica de granularidad y disciplina estricta de paridad primero.

AC
Arturo Cárdenas
Fundador y Chief Data Analytics & AI Officer
20 de marzo de 2026 · Actualizado 20 de marzo de 2026 · 12 min de lectura
Cómo Validamos una Migración de Datos Financieros hasta el 0.002% de Precisión

Punto Clave

Migrando la analítica de ingresos de una empresa de seguridad en la nube de una plataforma BI legacy a dbt + Snowflake, corrimos modelos duales simultáneamente y los comparamos a granularidad mensual, por plan y por cliente usando queries de varianza con FULL OUTER JOIN. El proceso encontró 15 bugs. La meta era menos del 0.01%. Llegamos al 0.002% — menos de $2,000 de varianza total sobre el conjunto completo de datos de ingresos.

La primera corrida de comparación completa arrojó una diferencia de una discrepancia significativa.

Mismos datos. Mismo período. Dos sistemas. una discrepancia significativa de diferencia. En ese momento, "suficientemente cerca" deja de ser un estándar razonable y se convierte en un pasivo.

Esa discrepancia no fue un error de migración. Fue un descubrimiento — un incremento del 118% sin documentar aplicado a nivel de base de datos, sin ningún historial en git. La migración no introdujo el problema. Lo expuso.

Para eso sirve la validación en migraciones de datos financieros. No para confirmar que el código nuevo hace lo mismo que el código viejo. Sino para confirmar que el código nuevo replica el comportamiento del viejo — y después decidir, con aprobación explícita, qué hacer con los lugares donde ese comportamiento era incorrecto.

Terminamos la migración con menos de $2,000 de varianza total sobre el conjunto completo de datos de ingresos. Eso es 0.002%. Así le hicimos.


Logramos 0.002% de varianza (menos de $2,000 sobre el conjunto completo de datos de ingresos) en una migración de datos financieros de una plataforma BI legacy a dbt + Snowflake, corriendo modelos duales en paralelo y comparándolos en múltiples niveles de granularidad — mensual, por plan, por cliente — con un query estructurado de FULL OUTER JOIN. La metodología encontró 15 bugs en el proceso. La meta era menos del 0.01%. Llegamos al 0.002%.


Por qué la validación de migraciones financieras es diferente

La mayoría de las validaciones ETL verifican que los datos llegaron. La validación de migraciones financieras verifica que los cálculos coinciden.

Es un problema distinto. Un conteo de filas no te dice nada sobre si un nivel de precio se aplicó correctamente. Un null check no va a detectar un macro de trimestre fiscal que hace que Q2 abarque 15 meses en lugar de 3. Una prueba de schema no va a señalar un multiplicador regional que se aplicó al product line equivocado.

El kit estándar — pruebas de dbt, conteos de filas, validación de schema — es necesario pero no suficiente. Lo que necesitas además es un sistema paralelo: el output del legacy corriendo simultáneamente con el output nuevo, comparados fila a fila en cada nivel de granularidad que importa. No solo "¿coincide el total?", sino "¿coincide a nivel de mes, de plan, de cliente, y de una manera que me diga de dónde viene la varianza?"

También hay una cuestión de disciplina. La tentación en una migración es ir corrigiendo los bugs conocidos sobre la marcha. Ese es el orden incorrecto. Corregir y migrar al mismo tiempo hace que toda varianza sea ambigua: ¿es un error de migración, o cambié el comportamiento intencionalmente? No puedes saberlo. La secuencia correcta es paridad primero — replicar el sistema legacy exactamente, bugs incluidos — validar hasta varianza casi cero, y después corregir los bugs en PRs separados y revisados donde la intención es explícita.

Corrimos cuatro flujos de validación paralelos durante la migración:

  1. Validación de tarifas de precio — valores de seeds versus CASE statements hardcodeados del legacy
  2. Validación del modelo de ingresos — v1 versus v2 en cuatro tablas de hechos principales
  3. Validación de tablas de entrada — seed data versus las tablas de referencia de la plataforma BI legacy
  4. Reconciliación de calidad de datos — diagnósticos de deduplicación, investigación de anomalías en series de tiempo

El flujo 4 merece mención especial: investigamos un pico del 18.1% mes a mes en datos de uso. Seis queries de diagnóstico después, la respuesta era crecimiento legítimo — crecimiento orgánico en una región — no re-ingesta. Sin esa investigación, nos hubiéramos alarmado o lo habríamos ignorado. Ninguna de las dos opciones es correcta.


La solución: comparación de modelos duales

La metodología central es sencilla de enunciar y requiere disciplina para ejecutar. Construye el mismo output de dos formas independientes. Corre ambas en el mismo ambiente simultáneamente. Compara en cada nivel de granularidad. Cualquier varianza por encima del umbral es un bug hasta demostrar lo contrario.

Aquí está el proceso repetible, estructurado para equipos que corren una migración similar:

Paso 1: Establece una línea base de referencia. El sistema legacy es v1. Es lo que es — bugs y todo. Su output es el objetivo. Antes de escribir un solo modelo v2, asegúrate de poder hacer queries a v1 de forma confiable y repetible. Si el output de v1 difiere entre corridas, corrige eso primero.

Paso 2: Construye los modelos v2 en paralelo. v2 corre en el mismo ambiente que v1. No en un ambiente de desarrollo separado — el mismo. Esto no es negociable: las diferencias de ambiente (manejo de zonas horarias, conversión de tipos, precisión numérica) crean varianzas fantasma que ocultan las reales.

Paso 3: Corre el query de varianza con FULL OUTER JOIN en el nivel de granularidad más alto primero. Ingresos totales mensuales, v1 versus v2. Si pasa, profundiza. Si falla, entiende por qué antes de profundizar — es posible que una sola causa raíz explique todo.

Paso 4: Profundiza por plan, luego por cliente. Una varianza que desaparece al desglosarla por plan es un problema de cálculo en un nivel específico. Una varianza que persiste a nivel de cliente es un problema de completitud de datos — una cuenta faltante, una llave de join incorrecta, un error de deduplicación.

Paso 5: Define un umbral y mantenlo. Nuestro umbral fue < 0.01%. Cada comparación de modelos recibió PASS o FAIL. Sin criterio subjetivo, sin "suficientemente cerca dado lo complejo". FAIL significa que depuras antes de continuar.

Paso 6: Investiga la varianza cero con el mismo cuidado que la varianza alta. Un 0.00% exacto en cada período puede significar que los modelos son genuinamente idénticos, o puede significar que están tomando del mismo origen y aún no han divergido. Necesitas saber cuál es.

Paso 7: Documenta cada resolución de varianza. Cuando encuentres una varianza y la corrijas, registra qué era, qué la causó y cómo la resolviste. En una migración larga, ese registro se convierte en la pista de auditoría que Finanzas y cumplimiento van a pedir.


Profundidad técnica: los queries de validación

El query de comparación central usa FULL OUTER JOIN, no INNER JOIN. Esto importa. Un INNER JOIN silenciosamente descartaría cualquier mes donde un sistema tiene datos y el otro no — exactamente la clase de error que intentas detectar.

with v1 as (
  select period_month, sum(total_gross_revenue) as v1_gross
  from fct_revenue_v1
  group by period_month
),
v2 as (
  select period_month, sum(total_gross_revenue) as v2_gross
  from fct_revenue_v2
  group by period_month
)
select
  coalesce(v1.period_month, v2.period_month) as period_month,
  v1_gross, v2_gross,
  v2_gross - v1_gross as absolute_diff,
  case when v1_gross = 0 then 0
    else round((v2_gross - v1_gross) / v1_gross * 100, 4)
  end as percent_diff_pct,
  case when abs(percent_diff_pct) < 0.01 then 'PASS' else 'FAIL' end as status
from v1
full outer join v2 on v1.period_month = v2.period_month
order by period_month

ROUND(..., 4) rastrea la varianza hasta 0.0001%. Nuestro umbral era 0.01%, lo que significa que cualquier cosa por encima de 0.0001% aparece con precisión significativa. Una discrepancia de $100 en un dataset de el conjunto completo de datos de ingresos es visible en este query.

Cuando el query de nivel superior muestra FAIL, profundiza por plan:

with v1 as (
  select period_month, plan_name, sum(total_gross_revenue) as v1_gross
  from fct_revenue_v1
  group by period_month, plan_name
),
v2 as (
  select period_month, plan_name, sum(total_gross_revenue) as v2_gross
  from fct_revenue_v2
  group by period_month, plan_name
)
select
  coalesce(v1.period_month, v2.period_month) as period_month,
  coalesce(v1.plan_name, v2.plan_name) as plan_name,
  v1_gross, v2_gross,
  v2_gross - v1_gross as absolute_diff,
  case when v1_gross = 0 then 0
    else round((v2_gross - v1_gross) / v1_gross * 100, 4)
  end as percent_diff_pct
from v1
full outer join v2 using (period_month, plan_name)
where abs(v2_gross - v1_gross) > 0.01
order by abs(absolute_diff) desc

El filtro WHERE abs(...) > 0.01 limpia el output. Quieres ver las varianzas que vale la pena investigar, no cada plan con una diferencia de redondeo de $0.004.

Para correr todos los pares de modelos de una sola vez, el resumen comprensivo de PASS/FAIL:

-- Summary with PASS/FAIL per model
select
  model_name,
  periods_compared, min_period, max_period,
  max_gross_diff_pct,
  case when max_gross_diff_pct < 0.01 then 'PASS' else 'FAIL' end as gross_status,
  max_net_diff_pct,
  case when max_net_diff_pct < 0.01 then 'PASS' else 'FAIL' end as net_status
from validation_summary
order by max_gross_diff_pct desc

Para la validación de tablas de referencia — seeds de precios versus tablas de tarifas del legacy — el patrón de anti-join detecta discrepancias a nivel de fila que las comparaciones agregadas no captarían:

select 'IN SEED NOT IN LEGACY' as diff_direction, s.*
from dbt_dev.discount_rates s
where not exists (
  select 1 from input_db.discount_rates i
  where s.segment = i.segment
    and s.plan_name = i.plan_name
    and s.discount_multiplier = cast(i.discount_multiplier as number(5,4))
    and equal_null(s.notes, i.notes)
)
union all
select 'IN LEGACY NOT IN SEED' as diff_direction, i.*
from input_db.discount_rates i
where not exists (
  select 1 from dbt_dev.discount_rates s
  where i.segment = s.segment
    and i.plan_name = s.plan_name
    and cast(i.discount_multiplier as number(5,4)) = s.discount_multiplier
    and equal_null(i.notes, s.notes)
)

equal_null() maneja el caso donde ambos lados son NULL — el = estándar fallaría en comparaciones null-versus-null y generaría falsos positivos. El cast(... as number(5,4)) explícito maneja el desajuste de tipos entre la inferencia de seeds y los tipos de columna del legacy.

Este query corrió contra cada archivo de seed antes de cualquier validación de modelos downstream. Nuestra comparación de tarifas de precio regresó en 0.00% — coincidencia exacta — una vez que todos los seeds fueron tipados correctamente.


Lecciones aprendidas

Paridad primero, corrección después — siempre. Encontramos una definición de trimestre fiscal que abarcaba 15 meses en lugar de 3. La respuesta correcta fue replicar el bug, lograr paridad, y después corregirlo en un PR separado. La historia completa está en Cuando la Respuesta Correcta es Código Incorrecto. Si corriges y validas en el mismo cambio, no puedes saber si una varianza restante es un error de migración o una mejora intencional. Sepáralos. Siempre.

La metodología de validación también es la metodología para encontrar bugs. Encontramos 15 bugs durante la migración — problemas de calidad de datos, de granularidad, fallos específicos de la plataforma, brechas en la evolución del schema, errores de lógica. Ninguno fue encontrado por pruebas unitarias. Todos fueron encontrados por reconciliación. La comparación sistemática de v1 y v2 en múltiples granularidades es la prueba que detecta lo que las pruebas de schema no capturan. Consulta 15 Bugs Silenciosos que Encontramos Migrando una Plataforma BI Financiera para el desglose completo.

NUMBER(38, 9) en todas partes, sin excepciones. Las columnas de punto flotante en modelos de ingresos son un riesgo de auditoría silencioso. El error no aparece de inmediato — se acumula como diferencias de redondeo a través de miles de filas hasta que Finanzas pregunta por qué el cierre de Q3 está $8,000 desviado. Lo documentamos como una regla estricta desde el día uno: todas las columnas financieras usan NUMBER(38, 9), todos los cálculos de varianza redondean a cuatro decimales.

Investiga los picos antes de asumir que son problemas. El pico del 18.1% en la serie de tiempo podría haber sido un problema real de calidad de datos o crecimiento legítimo del negocio. Corrimos seis queries de diagnóstico antes de concluir que era crecimiento legítimo. El costo de esa investigación fue unas horas. El costo de clasificar mal el crecimiento legítimo como un bug — o un bug como crecimiento legítimo — es mucho mayor.

La investigación de anomalías pertenece al alcance de la validación. No trates las preguntas de calidad de datos como algo separado de las preguntas de migración. Son la misma pregunta: ¿el nuevo sistema representa con precisión lo que ocurrió?


Preguntas frecuentes

¿Es 0.002% realmente suficiente para datos financieros?

Depende del monto absoluto en dólares y del uso downstream. Para el conjunto completo de datos de ingresos, 0.002% son menos de $2,000 — menos que el redondeo en la mayoría de los pronósticos manuales. Nuestra meta era < 0.01%, lo que hubiera sido $8,400. Ambas cifras están bien dentro de las tolerancias típicas de reportes financieros. La pregunta más importante es si entiendes de dónde viene la varianza. Una varianza inexplicada del 0.001% es peor que una varianza explicada del 0.05%. Conoce qué está impulsando cada número.

¿Qué haces si no puedes construir dos modelos simultáneamente?

El enfoque de modelo dual requiere correr ambos sistemas en el mismo ambiente, lo que no siempre es posible. Si no puedes correr v1 y v2 en paralelo, la alternativa es exportar snapshots conocidos y correctos del sistema legacy antes de la migración — totales de fin de período en cada nivel de granularidad — y validar el output del nuevo sistema contra esos snapshots. Pierdes algo de profundidad de diagnóstico (no puedes profundizar dinámicamente), pero preservas la verificación fundamental: el nuevo output debe coincidir con el output conocido-correcto dentro de la tolerancia.

¿Cómo manejas los casos donde el sistema legacy tiene bugs que quieres corregir?

La disciplina de paridad primero responde esto. Primero, construye una v2 que replique v1 exactamente — bugs incluidos. Valida hasta varianza casi cero. Después, en una rama separada con aprobación explícita, construye la versión corregida. Corre una comparación de tres vías: legacy, v2 (paridad) y v2 (corregida). El modelo de paridad te dice qué hubiera producido migrar sin cambios. El modelo corregido te dice qué realmente quieres. Documenta qué bugs se corrigieron y por qué. Consulta Cuando la Respuesta Correcta es Código Incorrecto: Replicación Intencional de Bugs para un recorrido detallado de este patrón.

¿Cuántos niveles de granularidad necesitas validar?

Como mínimo: total por mes, desglose por tu dimensión categórica principal (plan, línea de producto o segmento), y desglose por cuenta/cliente. Tres niveles. El primero detecta errores gruesos. El segundo aísla qué categoría los está generando. El tercero te dice si es un problema de cálculo (afecta toda una categoría) o un problema de completitud de datos (cuentas específicas faltantes o duplicadas). Si tu negocio tiene más complejidad de granularidad — multiplicadores regionales, tipos de contrato, segmentos fiscales — agrega niveles de granularidad en consecuencia. Cada dimensión que puede hacer que un cálculo difiera entre sistemas es un nivel de granularidad que vale la pena validar.

¿Cuánto tiempo toma en la práctica correr este framework de validación?

Escribir los queries la primera vez: un día, quizás dos, para una migración de complejidad moderada. Correrlos e interpretar los resultados: dos a cuatro horas por ciclo de validación. El costo continuo es bajo — los queries se vuelven a correr en minutos y los patrones se reutilizan entre modelos. La inversión inicial se recupera la primera vez que la validación detecta un bug que hubiera llegado a producción. En esta migración, detectó 15.


Cierre

El 0.002% de precisión es un resultado. La metodología que lo produjo — modelos paralelos, validación jerárquica de granularidad, disciplina de paridad primero, PASS/FAIL basado en umbrales — es lo que es reutilizable.

Las migraciones financieras no fallan porque el SQL estuvo incorrecto. Fallan porque la validación no fue lo suficientemente rigurosa para detectar dónde el SQL estaba sutilmente incorrecto de una manera que parecía correcta hasta el cierre trimestral.

Construye ambos modelos. Compara todo. Investiga cada varianza. El framework es sencillo. La disciplina para seguirlo es lo que separa las migraciones que aterrizan limpiamente de las que sacan a la luz problemas seis meses después.

Para la historia completa del proyecto, consulta el caso de estudio. Para cómo gestionamos el sprint técnico que produjo estos modelos, consulta Por Qué Claude Code Falla al 60% — y el Sistema que Nos Permitió Entregar de Todas Formas. Para cómo construir reconciliation tests y las otras tres categorías de tests en tu pipeline CI de dbt, consulta Mejores Prácticas de Testing en dbt para Datos Financieros.


¿Planeas una migración de datos donde los números tienen que pasar auditoría? Ya construimos la metodología de validación para lograrlo. Platiquemos sobre metodología.

Temas

validación migración datos financierosprecisión migración dbt Snowflakereconciliación datos de ingresosvalidación modelo dualquery FULL OUTER JOIN varianzaparidad primero migraciónmigración BI financieraframework validación migraciónpruebas modelo dbt ingresosprecisión migración datos
Compartir este artículo:
AC

Arturo Cárdenas

Fundador y Chief Data Analytics & AI Officer

Arturo es un consultor senior en analítica e IA que ayuda a empresas medianas y grandes a eliminar el caos de datos para desbloquear claridad, velocidad y ROI medible.

¿Listo para convertir datos en decisiones?

Hablemos de cómo lograr ROI medible en meses.