31 lecturasFintech · Ledger · Automatización

Un interruptor nuevo sobre datos viejos no es una función: es una migración

El módulo de liquidación estaba construido, revisado, desplegado y probado. Faltaba una sola cosa: encender la bandera en producción.

Es un cambio de un carácter. false a true.

Antes de tocarla hice la pregunta que no estaba en el ticket: ¿qué se mueve en la primera corrida?

Nadie la había hecho. Y la respuesta estaba a un dump de distancia.

Lo que había del otro lado del interruptor

Veinticinco órdenes acreditadas y sin liquidar. Meses de pagos entrando sin que nada saliera, porque el proceso que debía moverlos todavía no existía.

Lo importante no fue el número. Fue que esas veinticinco no eran un grupo. Eran dos problemas opuestos viviendo en la misma tabla.

Unas tenían la fecha de liberación ya vencida. Al encender la bandera, el proceso las tomaría todas juntas, en la primera corrida, en un solo lote. Meses de dinero moviéndose de golpe un martes cualquiera, porque el reloj dijo que tocaba — sin que ninguna persona hubiera decidido que ese día era el día.

Las otras no tenían fecha en absoluto. Y a ésas el proceso no las tomaría nunca. La fecha se sella en el momento de acreditar; encender el automático no rellena hacia atrás. Iban a quedarse retenidas para siempre, en silencio, sin un error en ningún log, sin aparecer en ninguna alerta. Dinero de comercios congelado por omisión.

Un mismo interruptor. Un grupo que se dispara sin permiso y otro que no se dispara jamás. Los dos comportamientos son el mismo bug: nadie miró el estado previo.

Y de paso apareció un tercero: el saldo retenido superaba lo que reclamaban las órdenes por el importe exacto de una de ellas. Un crédito duplicado. No un redondeo, no un descuadre difuso — una orden contada dos veces.

Por qué el software nuevo no ve esto

Cuando construyes una función, tu cabeza está en el flujo: llega un pago, se acredita, se sella la fecha, se cumple el plazo, se libera. Escribes tests para ese flujo. Todos pasan. El código es correcto.

Y lo es. Para los datos que van a nacer después.

El problema es que un proceso automático no se estrena contra un sistema vacío. Se estrena contra todo lo que pasó mientras no existía. Ese histórico no cumplió tus invariantes porque tus invariantes no existían cuando se escribió.

Un interruptor nuevo sobre datos viejos no es una función nueva. Es una migración. Y una migración que nadie llamó migración es una migración que nadie revisó.

Lo que hicimos en vez de encender

Separar antes de mover. Lo que se mueve solo por un camino, lo que no se mueve nunca por otro:

  • Los vencidos → al proceso automático, pero conscientemente, sabiendo cuánto y cuándo.
  • Los que el proceso jamás tomaría → a una mesa manual en el backoffice, donde una persona los ve y decide. No hay forma de automatizar algo que carece del dato que la automatización necesita.

Dos correcciones más salieron de mirar de cerca:

El importe que se liquida es el neto que efectivamente entró, no el bruto que puso el pagador. Mover el bruto habría liberado comisiones que no son del comercio. Son dos columnas parecidas en la misma fila, y elegir la equivocada regala dinero de forma sistemática y silenciosa.

Las banderas del automático salieron del árbol del backoffice. Estaban colgando del mismo nodo que el menú. Apagar una pantalla no puede apagar el motor que mueve el dinero: son dos decisiones distintas y merecen dos interruptores distintos.

La pregunta que hago ahora

Antes de encender cualquier proceso automático sobre dinero:

¿Cuántas filas va a tomar en su primera corrida, y cuántas no va a tomar nunca?

Si no puedes responder con una consulta, todavía no estás listo para encenderlo. No porque el código esté mal — probablemente está bien. Porque no sabes contra qué lo estás soltando.

La versión corta, para pegar en la pared: encender es instantáneo, deshacerlo no existe.

(En LedgerCore todo movimiento es un asiento con su fecha y su referencia, así que "qué va a mover este proceso" es una consulta y no una arqueología. Esa es toda la diferencia entre medir y adivinar.)

Compartir

Comentarios

Todavía no hay comentarios. Empieza tú.

No se publica. Solo lo uso para responderte.

Los comentarios se revisan antes de publicarse.