Principios de ingeniería para construir sistemas financieros
(substack.wasteman.codes)- La contabilidad no ha cambiado mucho en los últimos cientos de años
- Aun así, existe mucha confusión sobre la forma correcta de construir software para sistemas financieros
- Este artículo comparte lecciones basadas en la experiencia de construir sistemas financieros en grandes empresas
- Aunque se enfoca en la construcción de sistemas contables, estos principios también aplican a sistemas financieros más generales
Definiciones básicas de términos financieros
- Libro mayor general (General Ledger, GL): el principal registro contable de una empresa que resume todas las transacciones financieras durante un periodo determinado. Puede verse como el agregado de sus sub-ledgers correspondientes
- Sub-ledger: contiene detalles de todas las transacciones individuales relacionadas con un GL específico. Los registros del sub-ledger contienen datos mucho más granulares que el libro mayor general (por ejemplo, un cliente específico, una línea específica de un pedido, etc.). La diferencia de datos entre el sub-ledger y el GL varía según el tipo de negocio y el volumen de datos con el que se trabaje. Algunas empresas pequeñas pueden operar sin sub-ledger, pero si la escala es pequeña, es poco probable que necesiten software personalizado
- Registro financiero (Financial Record): término colectivo para referirse al libro mayor general y los sub-ledgers
- Materialidad (Material): indica si una distorsión en la información de los estados financieros afecta la toma de decisiones de una parte interesada razonable. Esta definición es intencionalmente algo ambigua, ya que distintas empresas tienen distintos umbrales de materialidad. Por ejemplo, algo material para una empresa con ingresos anuales de 250 mil dólares puede no serlo para una con ingresos anuales de mil millones. Desde una perspectiva de diseño, el principal valor de este concepto es clasificar distintas categorías de datos financieros
Flujo de datos de alto nivel
Business System --(Financial Events)--> Sub Ledger(s) --(Summarized Accounting Entries)--> General Ledger
Los tres objetivos principales de un sistema contable
- Precisión (Accurate): los registros financieros deben reflejar el estado conocido del negocio
- Ej.: si se vendieron 10 productos de $9.99, el total de ese registro financiero debe ser $99.90
- Esto parece obvio, pero al agregar miles o millones de transacciones, simples errores de suma entre sistemas o de redondeo pueden provocar inexactitudes importantes
-
Nota de Wasteman
- La gente dice que poner nombres es el problema más difícil en ciencias de la computación, pero yo creo que sumar es el segundo más difícil
- En los últimos años, trabajando en sistemas financieros a gran escala, he visto incontables veces cómo bugs diminutos generan grandes diferencias en los datos
- Ni hablemos de sumar
float. Aprendí por las malas por qué siempre hay que usar enteros
- Los registros financieros deben ser completos (complete)
- Más concretamente, los sub-ledgers y el libro mayor general deben representar por completo toda la actividad del negocio ocurrida en un momento determinado
- Si hubo eventos que ocurrieron pero no están en los registros financieros, entonces el sistema no está completo
- Esto no significa que la consistencia eventual no esté permitida
- Hay que saber cuándo los datos estarán completos para poder informar a las partes interesadas que los datos ya quedaron cerrados
-
Nota de Wasteman
- Garantizar la completitud también es un problema sorprendentemente difícil
- A medida que el sistema escala, los datos pueden deformarse accidentalmente o perderse al pasar por múltiples sistemas
- Auditabilidad (Auditable): los registros financieros deben poder auditarse con facilidad para que las partes interesadas detecten errores y puedan medir con precisión el desempeño del negocio
- Oportunidad (Timely): el sistema contable debe satisfacer las necesidades específicas del negocio
- Para una empresa pequeña, puede bastar con volcar todos los números al final del mes, pero las grandes empresas normalmente quieren sistemas casi en tiempo real
- Esto les permite monitorear la salud financiera durante todo el mes, tomar decisiones más rápido con base en datos financieros y reducir la presión de cerrar a inicios de mes o de trimestre
- Sea cual sea la necesidad, nuestro sistema contable debe cumplir con los requerimientos del negocio y con lo que oportuno signifique para ellos
-
Nota de Wasteman
- La gente tiende a perderse en discusiones de sistemas batch vs. streaming cuando se habla de oportunidad
- En mi opinión, en la mayoría de los sistemas esa no es una distinción importante
- Sí importa si te preocupan latencias muy cortas, de segundos a minutos
- Pero sorprende con qué frecuencia escucho a personas debatir qué hacer cuando el consumidor no necesita ver actualizaciones más de unas cuantas veces al día
- Que lo pidan no significa necesariamente que lo necesiten
Los tres principios de ingeniería principales que debe seguir un sistema contable
- Inmutabilidad (Immutability) y durabilidad (Durability) de los datos
- Permiten la auditabilidad, lo que ayuda a depurar y a mantener la precisión
- Si los datos son inmutables, se puede registrar el estado del sistema en cualquier momento
- Esto hace que recalcular el mundo desde un estado previo sea realmente fácil, porque no se pierde ningún estado
- Una vez que un dato queda consignado en un registro financiero, no puede eliminarse
- Toda corrección en el sistema debe representarse como una nueva transacción financiera
- Ej.: si había un bug en el sistema y por error se reportó que un servicio vendido en $900 se vendió en $1000
- Para corregir ese error, primero hay que revertir el asiento contable correspondiente al error y luego volver a registrar el asiento con el monto correcto
- Los datos deben registrarse con el menor nivel de granularidad posible (Data recorded at the smallest grain)
- Al igual que el principio anterior, esto también es crucial para permitir una trazabilidad de auditoría clara
- Aunque los reportes financieros y el libro mayor general se agreguen, se calculan a partir de eventos más granulares
- Cuando los datos no se entienden, se necesita el dato más granular para depurar cuál fue el problema
- Almacenar los datos con el nivel más bajo de granularidad también hace muy fácil corregir datos derivados de ese dataset
- Si un único dataset inmutable es la fuente central de verdad para todas las vistas de esos datos,
- para corregir una vista, basta con corregir los datos y volver a ejecutar el pipeline que genera esa vista
- De forma similar, cuando los contadores se preparan para cerrar los libros,
- concilian todas las transacciones ocurridas y los saldos de las cuentas para verificar que los libros sean correctos
- si encuentran una discrepancia, pueden profundizar hasta la transacción exacta que está causando el problema
- Debe ser idempotente (Idempotency)
- Cada evento financiero solo puede procesarse una vez, y la duplicación en los registros financieros provocará inexactitudes evidentes
- Por esta razón, todo el código que genera registros financieros debe ser idempotente
- Idempotente significa que aunque una operación se aplique varias veces, el resultado no cambia
- Es decir, aunque un evento financiero se procese varias veces, el resultado debe ser igual al de la primera vez
Buenas prácticas
- Preferir enteros para representar montos financieros: las operaciones aritméticas se vuelven mucho más sencillas. Evita
float - Soportar suficiente granularidad en los montos financieros para minimizar la pérdida de precisión al convertir divisas
- Si solo trabajas con dólares, puede bastar con representarlos en centavos
- Para negocios globales, se prefieren micro-unidades o decimales como
DECIMAL(19, 4) - Aunque elegir decimales es popular en sistemas financieros, en sistemas financieros de publicidad los micros son el estándar
- Usar un método de redondeo consistente: según cómo redondees, pueden surgir diferencias importantes respecto al monto esperado
- El método de redondear al siguiente dígito válido si es 5 o más, y bajar si es 4 o menos
- O el método de redondear siempre hacia arriba, etc.
- Lo importante es mantener la consistencia en todo el sistema (si hay una diferencia de 1 centavo por transacción, en 10 millones de transacciones eso son $100k)
- Retrasar la conversión de divisas tanto como sea posible: convertir una moneda por adelantado puede provocar pérdida de precisión
- Retrasa la conversión hasta después de que la agregación ocurra en la moneda local
- Usar una representación entera del tiempo: es un poco controvertido, pero muy recomendable
- Distintas librerías parsean los timestamps como objetos y cada una los maneja de manera diferente
- Conviene evitar ese dolor de cabeza y usar enteros
- Un timestamp Unix o un datetime entero basado en UTC funcionan perfectamente
- Mientras menos conversiones de datos haya entre sistemas, mejor
-
Nota de Wasteman
- Ni siquiera he mencionado los bugs relacionados con el horario de verano. Si usas enteros crecientes, puedes evitarlos por completo
- Si insistes en usar
datetime, por lo menos usa UTC. Sorprende cuántas empresas muy grandes siguen usando timestamps que no están en UTC
2 comentarios
Esto es realmente muy útil. Si haces conversiones de tipo (
decimal,float,double) o redondeos sin pensarlo y sin discutirlo con el equipo en un entorno real, puede terminar muy mal.Comentarios de Hacker News
Se enfatiza la importancia de usar una metodología de redondeo consistente
Se recomienda representar el tiempo como enteros
Se recomienda usar una base de datos relacional en sistemas contables
Se menciona que los principales objetivos de un sistema contable son exactitud, auditabilidad y oportunidad
Opinión sobre la integridad de los sistemas contables
Para negocios globales, se recomienda usar al menos 8 decimales
Se menciona la importancia de la interfaz de usuario (UI)
Se explica la diferencia entre procesamiento por lotes y procesamiento en streaming
Se comparte experiencia construyendo un sistema de facturación con TypeScript
Se recomienda usar las clases de la biblioteca estándar
Se explican las dificultades del redondeo y del intercambio de datos
Se comparte experiencia trabajando con APIs de 10 de los principales bancos de Estados Unidos
Se recomienda "Accounting Patterns" de Martin Fowler