15 Bugs Silenciosos que Encontramos Migrando una Plataforma de BI Financiero
Tu sistema de BI financiero en producción probablemente tiene bugs. Encontramos 15 en un sistema bien mantenido — ninguno habría sido detectado solo con pruebas.

Punto Clave
Migrar la plataforma BI legacy de una empresa de seguridad en la nube a dbt + Snowflake destapó 15 bugs silenciosos que habían distorsionado datos de ingresos, ARR y precios durante meses — algunos durante años. Una discrepancia de una discrepancia significativa en precios. Un trimestre fiscal que duró 15 meses. Tres restricciones de Snowflake native que nadie documentó. Aquí están los quince, con el SQL.
Tu sistema de BI financiero en producción probablemente tiene bugs ahorita mismo. No del tipo que lanza errores — esos ya los sabrías. Los callados. Los que hacen que los ingresos de Q2 estén un poco mal cada trimestre y todo el mundo asume que es un tema de redondeo de divisas. Los que llevan tanto tiempo ahí que ya se volvieron estructurales. Los que solo aparecen cuando construyes un segundo sistema, lo corres en paralelo, y te preguntas por qué los números no cuadran.
Encontramos quince durante un engagement de cinco meses migrando la plataforma BI legacy de una empresa de seguridad en la nube a un stack moderno con dbt y Snowflake. Quince. En un sistema que había estado generando reportes financieros en producción durante años.
Esto es el resumen de un párrafo para quien necesite mandárselo a su engineering manager: migrar una plataforma de BI financiero a dbt + Snowflake destapó 15 bugs en producción que habían estado distorsionando silenciosamente datos de ingresos, ARR y precios durante meses — algunos durante años. Los encontramos mediante reconciliación sistemática: correr ambos sistemas en paralelo y profundizar donde los resultados no coincidían. La mayoría nunca habría aparecido solo con pruebas.
Por qué los bugs silenciosos son peores en sistemas financieros
Un bug en tu motor de recomendaciones significa que algunos usuarios ven contenido menos relevante. Un bug en tu modelo de ingresos significa que Finance cierra el trimestre con números incorrectos. El costo de estar equivocado escala con la seriedad con que tratas el output.
Los datos financieros tienen dos propiedades que hacen a los bugs silenciosos particularmente peligrosos. Primero, los consumidores tienen alta confianza: Finance no corre validaciones antes de una presentación ante el consejo. Confían en que el sistema está bien porque siempre ha estado bien — o eso parecía. Segundo, los errores suelen ser autoconsistentes: un bug que lleva suficiente tiempo en el sistema queda incorporado en las expectativas de todos. El número "correcto" es lo que sea que produzca el sistema, porque nadie tiene un punto de referencia.
La única forma de encontrar estos bugs es construir un segundo sistema y forzar la confrontación. Que es exactamente lo que es una migración. Y por qué el proceso de migración es, contraintuitivamente, la mejor auditoría que tus datos financieros van a tener jamás.
Aquí están los quince, con evidencia.
Bug #1: La Tasa de Precios Que No Existía en Source Control
Escondido desde: Desconocido — anterior al equipo actual Impacto: ~una discrepancia significativa de discrepancia en una sola línea de producto, un solo mes
Nuestro output del modelo dbt para los precios de una línea de producto en noviembre 2025: $398K. El output del sistema legacy para los mismos datos, mismo período: $870K. Una diferencia de una discrepancia significativa sin causa obvia inmediata.
Nos fuimos a fondo. Las tasas eran reales — el sistema legacy las estaba aplicando — pero existían solo en la base de datos del sistema legacy. No en su repositorio git. No en ningún archivo de configuración que alguien pudiera encontrar. Un multiplicador de 2.185x, que representa un incremento de precio del 118%, había sido aplicado a nivel de base de datos sin ningún cambio de código y sin ninguna documentación.
Lo ingeniería inversa dividiendo el output del sistema legacy entre el nuestro en cada nivel de precios hasta que el ratio fue consistente. El multiplicador se sostuvo. El fix fue aplicarlo en nuestro modelo. La lección más grande fue más difícil de asimilar.
El dato más peligroso es el que no puedes ver en source control.
-- Lo que el sistema legacy estaba haciendo (reconstruido — no existía código)
-- En algún lugar de una tabla de base de datos, no un config file, no un repo:
-- product_rate = base_rate * 2.185 ← esto vivía en un row de una tabla, no en código
-- Lo que encontramos al reconciliar:
select
product_line,
sum(legacy_revenue) as legacy_revenue,
sum(new_revenue) as new_revenue,
round(sum(legacy_revenue) / nullif(sum(new_revenue), 0), 4) as implied_multiplier
from revenue_reconciliation
where product_line = 'product_a'
and billing_period = '2025-11-01'
group by product_line
-- Resultado: implied_multiplier = 2.1850
-- Cada nivel de precios. Consistente. Un cambio de precio aplicado sin un commit.
Bug #2: Tres Mil Cuentas Que No Estaban Ahí
Escondido desde: ~2 meses Impacto: Clasificaciones de cuenta faltantes para octubre y noviembre 2025
Los datos de cuentas de octubre y noviembre 2025 todavía no existían en el CSV cuando construimos los modelos iniciales. Lo sabíamos, lo señalamos, y hicimos backfill de 3,381 cuentas con los datos disponibles.
La razón por la que esto llegó a la lista de bugs: el sistema legacy tenía el mismo vacío y nadie lo había notado. Se estaban calculando ingresos para registros de billing sin clasificación de cuenta correspondiente. Las cuentas no estaban perdidas — simplemente no clasificadas, lo que significaba que caían en un bucket genérico que nadie auditaba.
-- El grain check que lo encontró:
select
b.billing_period,
count(b.account_id) as billing_records,
count(a.account_id) as matched_accounts,
count(b.account_id) - count(a.account_id) as unmatched
from billing b
left join accounts a on a.account_id = b.account_id
group by b.billing_period
order by billing_period desc
-- Octubre 2025: 3,381 sin match
-- Noviembre 2025: 3,381 sin match
-- Cada mes anterior: 0 sin match
-- El sistema legacy tenía el mismo vacío. Nadie corrió esta query.
Bug #3: Los Ingresos de 2022 Que Siempre Estuvieron un Poco Mal
Escondido desde: ~3 años Impacto: Varianza sistemática en ingresos netos para cualquier cuenta con historial huérfano
Cuando corrimos la reconciliación para ingresos netos de 2022, encontramos una varianza consistente en un subconjunto de cuentas. No ruido aleatorio — las mismas cuentas, sistemáticamente desviadas. La causa raíz: lógica de fechas en cuentas huérfanas.
Las cuentas que habían tenido churn y reactivación tenían rangos de fechas que no conectaban limpiamente. La lógica del join usaba una fecha de "primera aparición" que, para estas cuentas, jalaba de un registro anterior a su reactivación. Los ingresos netos de esas cuentas se calculaban con el período efectivo incorrecto.
El fix fue directo: reescribir el join de cuentas huérfanas para usar la fecha de activación más reciente, no el mínimo histórico. Tres años de números levemente incorrectos en un puñado de cuentas. No suficiente para fallar una auditoría, sí suficiente para que las conversaciones de budget vs. actuals fueran confusas cada vez que salían esas cuentas.
Bug #4: Filas Duplicadas en Ingresos Mensuales
Escondido desde: Desconocido Impacto: Ingresos inflados donde los nombres de plan tenían múltiples IDs de plan
El grain del modelo legacy de ingresos mensuales debía ser una fila por cuenta, por plan, por mes. No lo era. El modelo incluía plan_id en su definición de grain, y algunos valores de plan_name mapeaban a múltiples valores de plan_id — múltiples versiones del mismo plan rastreadas por separado en el sistema fuente.
Resultado: los ingresos de esos planes aparecían dos veces. O tres. Sin importar cuántos valores de plan_id existieran para ese plan_name.
-- El bug: el grain incluía plan_id, que tiene múltiples valores por plan_name
select
account_id,
plan_name,
plan_id, -- ← este estaba en el GROUP BY. No debería estar.
billing_month,
sum(revenue) as revenue
from billing
group by account_id, plan_name, plan_id, billing_month
-- El fix: deduplicar antes de agregar
with deduped as (
select
account_id,
plan_name,
billing_month,
sum(revenue) as revenue
from billing
group by account_id, plan_name, billing_month
)
select * from deduped
-- Ahora una fila por (cuenta, plan_name, mes). Los ingresos dejan de inflarse.
Bug #5: El Trimestre Fiscal Que Duró 15 Meses
Escondido desde: ~2 años Impacto: Valores de ARR trimestral incorrectos, etiquetas de trimestre incorrectas en reportes
Una definición de trimestre fiscal que abarcaba 15 meses en lugar de 3 — la historia completa de cómo lo encontramos, lo replicamos intencionalmente, y después lo corregimos en un PR separado está en Cuando la Respuesta Correcta es Código Incorrecto.
Bug #6: El Filtro del Dashboard Que Solo Aplicaba en Una Página
Escondido desde: Desconocido — probablemente desde que se construyó el dashboard Impacto: Los usuarios que filtraban por período de tiempo solo filtraban la primera página
Este no es un bug de SQL — es un bug de configuración de dashboard, lo que significa que había sido visible para cada usuario del sistema legacy y nadie lo había notado, porque las otras páginas se veían bien.
Un control de filtro global tenía su alcance configurado solo para la página actual, no para todas las páginas. Los usuarios seleccionando un rango de fechas creían que estaban filtrando el dashboard completo. Tres de las cuatro páginas ignoraban el filtro completamente.
El fix: configurar el alcance del filtro a "todas las páginas." Treinta segundos. El bug había estado ahí desde que existía el dashboard.
Bug #7: El Trimestre Que Desapareció
Escondido desde: Poco tiempo — detectado durante el desarrollo Impacto: Los datos de Q1–Q3 FY26 desaparecieron completamente del ARR trimestral
Este apareció rápido, lo cual fue afortunado, porque su efecto era catastrófico: correr ARR trimestral y no obtener datos para tres trimestres.
La causa: una llamada a un macro dbt dentro de una cláusula PARTITION BY. En el executor nativo de dbt en Snowflake, esto compila sin error. La query corre. El particionamiento está mal. Los cálculos de trimestre fiscal producen basura, y el filtro QUALIFY ROW_NUMBER() = 1 encima de ellos elimina casi todo.
-- Lo que escribimos (compila bien, resultados incorrectos en Snowflake native):
select
account_id,
revenue,
{{ fiscal_quarter_macro('billing_date') }} as fiscal_quarter
from billing
qualify row_number() over (
partition by account_id, {{ fiscal_quarter_macro('billing_date') }}
-- ^^^ macro call en PARTITION BY = falla silenciosa
order by billing_date desc
) = 1
-- Fix: inline del cálculo, sin macro en window functions
select
account_id,
revenue,
case
when billing_date >= '2025-08-01' then 'Q3FY26'
when billing_date >= '2025-05-01' then 'Q2FY26'
-- ... etc
end as fiscal_quarter
from billing
qualify row_number() over (
partition by account_id,
case
when billing_date >= '2025-08-01' then 'Q3FY26'
-- inlineado, no una llamada a macro
end
order by billing_date desc
) = 1
La documentación del executor nativo de dbt en Snowflake no dice nada de esto. Ya tengo problemas de confianza.
Bug #8: El Patrón Elegante de Macro Que Snowflake Rechazó
Escondido desde: Poco tiempo — detectado durante el sprint de FY27 Impacto: Todos los macros de lookup temporal rotos en runtime
El patrón estándar de dbt para lookups temporales: subquery correlacionada. Limpia. Legible. Se genera naturalmente. Completamente sin soporte en el executor nativo de dbt en Snowflake.
Los macros compilaban. Se veían bien en el DAG. Fallaban en runtime con mensajes de error crípticos que no apuntaban inmediatamente a la subquery correlacionada como el problema. Para cuando diagnosticamos el Batch 4, ya habíamos perdido un día.
El fix fue arquitectónico: cada lookup temporal reescrito como JOIN + QUALIFY. No solo un workaround — el nuevo patrón fue mediblemente más rápido en el motor de ejecución de Snowflake. Pero descubrirlo no fue una tarde agradable.
-- Lo que funcionaba en todos lados (falla en runtime en Snowflake native):
select
t.*,
(
select r.rate
from pricing_rates r
where r.effective_date <= t.billing_date
order by r.effective_date desc
limit 1
) as applied_rate
from billing t
-- El reemplazo para Snowflake native:
select t.*, r.rate as applied_rate
from billing t
left join pricing_rates r
on r.effective_date <= t.billing_date
qualify row_number() over (
partition by t.billing_id
order by r.effective_date desc
) = 1
-- Más rápido. Pero tuvimos que aprenderlo a las malas.
Bug #9: El CSV Que Usaba Nombres Diferentes a la Base de Datos
Escondido desde: ~1 mes Impacto: Fallas en atribución de ingresos para las cuentas de un socio tecnológico importante
Un CSV de billing de un socio tecnológico importante usaba prefijos de nombre de plan diferentes a los registros correspondientes en las tablas fuente del sistema legacy. No valores diferentes — los mismos planes, las mismas cuentas, solo con prefijos distintos en el CSV que en la base de datos.
El JOIN falló silenciosamente: los registros que no hacían match caían en un bucket NULL en lugar de lanzar un error. Los ingresos de esas cuentas quedaron sin atribuir. El fix requirió una capa de normalización que eliminara y estandarizara prefijos antes de correr cualquier lógica de join.
-- El mismatch (ilustrativo — nombres anonimizados):
-- In legacy system: 'PRODUCT_detection_advanced'
-- En CSV: 'detection_advanced'
-- La falla silenciosa:
select c.*, p.account_classification
from csv_billing c
left join legacy_accounts p
on c.plan_name = p.plan_name -- ← falla silenciosamente, devuelve NULL
-- Sin error. Solo NULL en account_classification para cada registro del CSV.
-- El fix: normalizar antes de hacer join
select c.*, p.account_classification
from csv_billing c
left join legacy_accounts p
on regexp_replace(c.plan_name, '^PRODUCT_', '') = p.plan_name
Bug #10: Las Columnas de Ingresos en Unidades Diferentes
Escondido desde: Detectado temprano en el desarrollo de v2 Impacto: Mismatch de unidades entre v1 y v2 causando falsas alarmas de varianza
Cuando construimos los modelos de ingresos v2, la reconciliación mostró una discrepancia consistente de 1000x en todos los registros. No un error de lógica — unidades. El modelo v1 reportaba ingresos en dólares. El modelo v2, siguiendo lo que parecía un default razonable según el nombre de la columna fuente, reportaba en milicéntimos.
No fue un bug sutil. Se encontró de inmediato cuando la primera reconciliación mostró que v2 era 1000x mayor que v1 en cada fila. Se corrigió en minutos. Está en la lista porque es uno de los errores más comunes en migraciones de sistemas financieros, y "déjame revisar las unidades" es un paso que se salta bajo presión de tiempo.
Bug #11: El UNION ALL Que Creó Sus Propios Duplicados
Escondido desde: Detectado durante el desarrollo de v2 Impacto: Filas duplicadas de ARR cuando un cliente aparecía en datos de antes y después del cutover
Para manejar un cambio de schema ocurrido en octubre 2025, construimos el modelo ARR v2 usando un UNION ALL de datos pre y post cutover. El UNION ALL era correcto. El problema: algunos registros de clientes aparecían en ambas tablas durante el período de transición, porque el cutover no fue instantáneo.
Resultado: los clientes activos durante la ventana de transición aparecían dos veces en el output unido. El ARR de esos clientes se duplicó. Sin error, sin advertencia — solo ARR inflado para un cohorte específico de clientes en un rango de fechas específico.
-- El bug: UNION ALL sin verificar solapamiento
select account_id, billing_date, arr_value, 'pre_cutover' as source
from arr_schema_v1
where billing_date < '2025-10-01'
union all
select account_id, billing_date, arr_value, 'post_cutover' as source
from arr_schema_v2
where billing_date >= '2025-10-01'
-- Problema: ¿qué pasa si arr_schema_v2 también tiene registros antes del 2025-10-01?
-- (Los tenía. Para algunas cuentas. El cutover tuvo solapamiento.)
-- Fix: deduplicación explícita al final
with unioned as (
-- mismo union de arriba
),
deduped as (
select *
from unioned
qualify row_number() over (
partition by account_id, billing_date
order by case when source = 'post_cutover' then 1 else 2 end
) = 1
)
select * from deduped
Bug #12: La Prioridad de Descuentos Que Resolvía en el Orden Incorrecto
Escondido desde: Desconocido — presente en el sistema legacy Impacto: Ingresos netos incorrectos para cuentas con reglas de descuento solapadas
La lógica de descuentos tenía un sistema de prioridades: cuando múltiples reglas de descuento aplicaban a una cuenta, la regla de mayor prioridad debía ganar. La prioridad de applies_to no resolvía correctamente — una regla de menor prioridad ganaba para una clase específica de configuraciones de cuenta.
El fix requirió reconstruir la resolución de prioridades como un ROW_NUMBER() explícito ordenado por rango de prioridad, en lugar de confiar en el ordenamiento implícito de cómo se evaluaban las reglas. Sutil, difícil de probar sin conocer las configuraciones exactas de cuenta donde se manifestaba, y exactamente el tipo de cosa que produce conversaciones de "algo parece raro con los números de estas cuentas específicas" cada trimestre.
Bug #13: La Auditoría Regional Con Rangos de Fechas Solapados
Escondido desde: Detectado durante el desarrollo de la auditoría de datos Impacto: Doble conteo en auditorías de ingresos regionales para el período de transición
Al construir un modelo de auditoría de datos regional, lo construimos uniendo datos regionales de diferentes períodos de tiempo. Los rangos de fechas se solapaban. Los registros del mes límite aparecían en el conteo de ambas regiones.
No fue sutil una vez que lo viste. Los totales regionales no sumaban al total global. Por exactamente el volumen del mes solapado, para exactamente las regiones afectadas. El fix fue limpiar los rangos de fechas — asegurando que la union cubriera cada fecha exactamente una vez.
-- El bug: rangos de fechas solapados en un union regional
select 'apj' as region, account_id, revenue from apj_data
where billing_date between '2024-01-01' and '2024-06-30'
union all
select 'apj' as region, account_id, revenue from apj_data_updated
where billing_date between '2024-06-01' and '2024-12-31'
-- ^^^^ Junio aparece en ambos rangos
-- Fix: rangos no solapados
select 'apj' as region, account_id, revenue from apj_data
where billing_date < '2024-06-01'
union all
select 'apj' as region, account_id, revenue from apj_data_updated
where billing_date >= '2024-06-01'
Bug #14: Australia Cambió Su Schema y No Le Avisó a Nadie
Escondido desde: ~2 años (desde octubre 2023) Impacto: Todos los datos históricos de Australia faltando del modelo de staging
En octubre 2023, la región Australia ([código-de-región]) migró de un formato de schema plano a un formato estructurado [versión-de-schema]. El modelo de staging había sido actualizado para manejar [versión-de-schema]. Ya no manejaba el formato plano previo a la migración.
Cada query que incluía datos históricos de Australia excluía silenciosamente todo lo anterior a octubre 2023. Sin error. Sin advertencia. Solo un modelo de staging con una cláusula WHERE que solo hacía match con el nuevo formato, y un LEFT JOIN que devolvía NULLs para el antiguo.
El fix: un modelo de staging híbrido que une con UNION ALL ambos formatos, con una fecha de cutover explícita de octubre 2023. No siempre puedes confiar en que los sistemas fuente tienen una evolución de schema limpia y monotónica. La realidad es más desordenada que el diagrama de schema.
Bug #15: El NULL en Tipo de Cuenta Que No Era Culpa de Nadie
Escondido desde: Detectado tarde en el engagement Impacto: Clasificación de tipo de cuenta faltante en un subconjunto de registros
El bug final: un LEFT JOIN entre registros de billing y la tabla de clasificación de cuentas no hacía match en todos los registros. Algunas cuentas de billing existían en el modelo CSV pero no en la tabla de referencia de cuentas, dejando account_type como NULL.
No fue un error de lógica en el join. No fue un problema de calidad de datos en sentido estricto. Fue un problema de cobertura: la tabla de cuentas se construyó desde una fuente de datos, el CSV de billing era una fuente de datos diferente, y la cobertura no era completa. El fix fue identificar los registros sin match y ya sea hacer backfill de las cuentas faltantes o agregar un catch-all explícito para registros no clasificables conocidos.
El meta-patrón: 15 bugs, 6 categorías, 1 metodología
Agrupa estos quince bugs y obtienes un panorama de dónde fallan realmente los sistemas de BI financiero:
| Categoría | Cantidad | Qué sale mal |
|---|---|---|
| Calidad de datos / datos faltantes | 4 | Cuentas, tasas, vacíos de cobertura |
| Grain y deduplicación | 3 | Nivel de detalle incorrecto, filas duplicadas, mismatch de unidades |
| Restricciones específicas de plataforma | 3 | Limitaciones del executor nativo de dbt en Snowflake |
| Errores de lógica | 3 | Límites de fechas, resolución de prioridades, calendarios fiscales |
| Configuración de dashboard | 1 | Alcance del filtro — el invisible |
| Evolución de schema | 1 | Las fuentes cambian; los modelos de staging no siempre siguen |
Doce de los quince se encontraron mediante reconciliación — corriendo ambos sistemas en paralelo y profundizando sistemáticamente donde los outputs no coincidían. Los otros tres se detectaron durante el desarrollo cuando algo se veía mal. Ninguno se encontró mediante pruebas unitarias. Ninguno lo habría sido.
La metodología que hace surgir estos bugs está descrita completa en nuestro post sobre validar una migración financiera a 0.002% de precisión. La versión corta: corre ambos sistemas, compara outputs a múltiples grains — totales mensuales, luego por cliente, luego por línea de producto. Una varianza que desaparece cuando profundizas es un problema de grain. Una varianza que persiste en todos los niveles es un error de lógica. La jerarquía reduce significativamente el tiempo de debugging.
No encontramos estos bugs porque seamos especialmente buenos encontrando bugs. Los encontramos porque estábamos buscando. Y estábamos buscando porque teníamos un segundo sistema contra qué comparar.
El crecimiento de alcance que nos llevó a descubrir bugs en los siete dominios es su propia historia.
Esa es la implicación incómoda: si nunca has migrado tu sistema de BI financiero, nunca has tenido un punto de referencia. Has estado confiando en un sistema que jamás ha sido validado externamente. Probablemente tienes alguna versión de varios de estos bugs. No quizás. Probablemente.
La pregunta es si los encuentras durante una migración controlada o durante una auditoría.
Preguntas frecuentes
¿Cómo encuentras bugs silenciosos sin migrar todo el sistema?
La versión ligera de la metodología es una comparación de modelo dual: construye una nueva versión de un modelo crítico junto al existente, córrelos con los mismos datos históricos, y usa un FULL OUTER JOIN para comparar outputs a múltiples grains. No necesitas una migración completa para aplicar esto — cualquier modelo del que sospechas puede interrogarse así. La clave es usar FULL OUTER JOIN (no INNER JOIN) para que los registros faltantes aparezcan como NULLs en lugar de desaparecer de la comparación. Empieza con tus modelos de mayor riesgo: ARR, ingresos netos, lo que alimenta la presentación al consejo.
¿Por qué las pruebas estándar no detectan estos bugs?
Las pruebas unitarias validan que el código hace lo que esperas que haga. Los bugs silenciosos son casos donde el código hace lo que esperabas, pero esperabas la cosa incorrecta. El Bug #5 (el trimestre de 15 meses) había estado mal tan consistentemente que las expectativas de todos se habían calibrado a él — el output "correcto" era el output incorrecto, porque el bug era anterior al marco de referencia de cualquiera. Los schema tests, los not-null checks y las range assertions no pueden detectar eso. Solo una comparación contra un sistema de referencia independiente puede.
¿Deberíamos preocuparnos por estos bugs específicos en nuestro setup de dbt + Snowflake?
Los bugs específicos de plataforma (Bugs #7 y #8) son restricciones del executor nativo de dbt en Snowflake que agarran a muchos equipos. Si estás corriendo dbt nativo en Snowflake, prueba soporte de subqueries correlacionadas y llamadas a macros en cláusulas PARTITION BY antes de meterte de lleno a un sprint. Los demás bugs son problemas generales de BI financiero que no son específicos de plataforma — errores de grain, problemas de dedup y cambios de tasas no documentados ocurren en cualquier stack. Los patrones de Snowflake native se cubren con más profundidad en nuestro post sobre replicación intencional de bugs.
¿Cuánto tiempo toma encontrar bugs como estos?
Encontrarlos tomó tres meses de ejecución en paralelo y reconciliación sistemática. Corregirlos varió desde treinta segundos (Bug #6, el alcance del filtro del dashboard) hasta varios días (Bug #8, el cambio arquitectónico de subqueries correlacionadas a JOIN + QUALIFY). La cola larga no son los fixes — es el diagnóstico. La mayoría de estos bugs tienen causas raíz no obvias que inicialmente parecen otra cosa. La varianza de cuentas huérfanas de 2022 parecía un problema de frescura de datos. El trimestre fiscal de 15 meses parecía un problema de zona horaria. Construir una metodología sistemática de drill-down (mensual → cliente → línea de producto) reduce significativamente el tiempo desde "algo está mal" hasta "aquí está exactamente por qué."
¿Cómo evitas que estos bugs se reintroduzcan después de la migración?
Tres cosas: documentación que viaja con el modelo (dbt persist_docs hace esto visible en la UI de Snowflake), un grain check que corre como parte del CI (una query que aserta una fila por combinación de clave esperada, fallando el build si encuentra duplicados), y seed files para cualquier cosa que actualmente viva como un valor hardcodeado en SQL. La tasa no documentada (Bug #1) habría sido prevenible si la tasa hubiera vivido en un seed file con historial de cambios. Vivía en un row de base de datos sin historial de commits. Ambas son consultables; solo una es auditable.
Los quince bugs que encontramos estaban en un sistema bien mantenido, construido por un equipo competente, que había estado generando reportes financieros en producción durante años.
Ese es el punto. Estos no son resultado de ingeniería deficiente. Son resultado de sistemas que nunca fueron obligados a defenderse contra una comparación. La migración fue la comparación.
Los encontramos porque estábamos buscando. La metodología que hizo sistemática esa búsqueda es todo el asunto. El framework de testing que habría detectado estos bugs antes de llegar a producción está en Mejores Prácticas de Testing en dbt para Datos Financieros.
Si tu sistema de BI financiero nunca ha sido migrado, casi seguro tiene bugs que nadie ha encontrado. Sabemos cómo sacarlos a la luz. Agenda una evaluación de migración.
Temas
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.


