34 pontos por GN⁺ 2024-07-12 | 2 comentários | Compartilhar no WhatsApp
  • A contabilidade não mudou muito ao longo dos últimos séculos
  • Mesmo assim, ainda há muita confusão sobre a forma correta de construir software para sistemas financeiros
  • Este texto compartilha lições baseadas na experiência de construir sistemas financeiros em grandes empresas
  • Embora o foco seja a construção de sistemas contábeis, esses princípios também se aplicam a sistemas financeiros de forma mais ampla

Definições de termos financeiros básicos

  • Livro razão geral (General Ledger, GL): o principal registro contábil de uma empresa, que resume todas as transações financeiras de um determinado período. Pode ser visto como a agregação dos respectivos sub-ledgers
  • Sub-ledger: contém os detalhes de todas as transações individuais relacionadas a um GL específico. Os registros de um sub-ledger incluem dados muito mais granulares do que o livro razão geral (por exemplo, um cliente específico, um item específico de um pedido etc.). A diferença de dados entre o sub-ledger e o GL varia conforme o tipo de negócio e o volume de dados em processamento. Algumas pequenas empresas podem operar sem sub-ledgers, mas, nesse porte, é pouco provável que precisem de software sob medida
  • Registro financeiro (Financial Record): termo coletivo para o livro razão geral e os sub-ledgers
  • Materialidade (Material): indica se uma distorção nas informações das demonstrações financeiras influencia a tomada de decisão de uma parte interessada razoável. Essa definição é intencionalmente um pouco vaga, porque empresas diferentes têm critérios de materialidade diferentes. Por exemplo, algo importante para uma empresa com receita anual de US$ 250 mil pode não ser importante para uma empresa com receita anual de US$ 1 bilhão. Do ponto de vista de design, o principal valor desse conceito é classificar diferentes categorias de dados financeiros

High Level Data Flow

Business System --(Financial Events)--> Sub Ledger(s) --(Summarized Accounting Entries)--> General Ledger

Três objetivos principais de um sistema contábil

  1. Precisão (Accurate): os registros financeiros devem refletir o estado conhecido do negócio
  • Ex.: se foram vendidos 10 produtos de US$ 9,99, o total nesses registros financeiros deve ser US$ 99,90
  • Isso pode parecer óbvio, mas, ao agregar milhares ou milhões de transações, simples somas entre sistemas ou erros de arredondamento podem gerar imprecisões relevantes
  • Nota do Wasteman

    • As pessoas dizem que naming é o problema mais difícil da ciência da computação, mas eu acho que soma vem logo depois
    • Nos últimos anos trabalhando com sistemas financeiros de grande escala, vi incontáveis casos em que bugs minúsculos causaram grandes diferenças nos dados
    • Nem comece a falar sobre somas com float. Aprendi da pior forma por que se deve sempre usar inteiros
  • Os registros financeiros devem ser completos (complete)
    • Mais especificamente, os sub-ledgers e o livro razão geral devem representar integralmente toda a atividade de negócio ocorrida em um determinado momento
    • Se houve eventos que ocorreram, mas não estão nos registros financeiros, então o sistema não está completo
    • Isso não significa que consistência eventual (eventual consistency) não seja permitida
    • É preciso saber quando os dados estarão completos para poder informar às partes interessadas que os dados foram fechados
  • Nota do Wasteman

    • Garantir completude também é um problema surpreendentemente difícil
    • À medida que o sistema cresce, os dados podem ser alterados por engano ou perdidos ao passar por vários sistemas
  1. Auditabilidade (Auditable): os registros financeiros devem ser fáceis de auditar, para que as partes interessadas consigam detectar erros e medir com precisão o desempenho do negócio
  2. Pontualidade (Timely): o sistema contábil deve atender às necessidades específicas do negócio
  • Para empresas pequenas, despejar todos os números no fim do mês pode ser suficiente, mas empresas grandes normalmente querem sistemas quase em tempo real
    • Isso permite monitorar a saúde financeira ao longo de todo o mês, tomar decisões baseadas em dados financeiros com mais rapidez e reduzir a correria para fechar o mês/trimestre no começo do período seguinte
  • Seja qual for a necessidade, nosso sistema contábil deve atender aos requisitos do negócio, exatamente no sentido em que pontual significa para ele
  • Nota do Wasteman

    • As pessoas tendem a se perder em discussões sobre sistemas batch versus streaming quando falam de pontualidade
    • Na minha visão, para a maioria dos sistemas isso não é uma distinção importante
    • Isso importa se você se preocupa com latências muito curtas, na faixa de segundos a minutos
    • Mas é surpreendentemente comum ouvir gente discutindo o que fazer quando o consumidor nem precisa ver atualizações mais do que algumas vezes por dia
    • Só porque alguém pediu não significa necessariamente que isso seja necessário

Três princípios de engenharia fundamentais que um sistema contábil deve seguir

  1. Imutabilidade (Immutability) e durabilidade (Durability) dos dados
  • Isso permite auditabilidade, ajudando na depuração e na precisão
  • Quando os dados são imutáveis, é possível registrar o estado do sistema a qualquer momento
  • Isso torna muito fácil recalcular o mundo a partir de um estado anterior, porque nenhum estado se perde
  • Uma vez que um dado esteja explicitado no registro financeiro, ele não pode ser apagado
  • Toda correção no sistema deve ser representada como uma nova transação financeira
    • Ex.: se houver um bug no sistema e um serviço que deveria ter sido vendido por US$ 900 tiver sido registrado por engano como vendido por US$ 1000
    • Para corrigir esse erro, primeiro é preciso estornar o lançamento contábil correspondente ao erro e depois refazer o lançamento com o valor correto
  1. Os dados devem ser registrados no menor nível de granularidade possível (Data recorded at the smallest grain)
  • Assim como o princípio acima, isso também é extremamente importante para permitir uma trilha de auditoria clara
  • Mesmo que os relatórios financeiros e o livro razão geral sejam agregados, eles são calculados a partir de eventos mais granulares
  • Quando os dados não fazem sentido, é preciso ter os dados mais granulares para depurar qual foi o problema
  • Armazenar os dados no menor nível de granularidade também facilita muito corrigir os dados derivados desse dataset
    • Se um único dataset imutável for a fonte central da verdade para todas as visões desses dados,
    • para corrigir uma visão, basta corrigir os dados e executar novamente o pipeline que gera essa visão
  • De forma semelhante, quando os contadores se preparam para fechar os livros,
    • eles conciliam todas as transações ocorridas e os saldos das contas para verificar se os livros estão corretos
    • se encontrarem divergências, podem investigar a transação exata que está causando o problema
  1. Deve haver idempotência (Idempotency)
  • Todo evento financeiro deve ser processado apenas uma vez, e duplicações nos registros financeiros causarão imprecisões óbvias
  • Por isso, todo código que gera registros financeiros deve ser idempotente
    • Idempotente significa que aplicar uma operação várias vezes não altera o resultado
    • Ou seja, mesmo que um evento financeiro seja processado várias vezes, o resultado deve ser igual ao do primeiro processamento

Boas práticas

  • Prefira inteiros para representar valores financeiros: as operações aritméticas ficam muito mais simples. Evite float
  • Dê suporte à granularidade de valores financeiros para minimizar perda de precisão em conversões cambiais
    • Se você lida apenas com dólares, representar em centavos pode ser suficiente
    • Para negócios globais, prefira microunidades ou números decimais como DECIMAL(19, 4)
    • Embora o uso de decimais seja popular em sistemas financeiros, microunidades são o padrão em sistemas financeiros de publicidade
  • Use um método de arredondamento consistente: dependendo da forma de arredondar, pode haver diferenças relevantes em relação ao valor esperado
    • Arredondar todos os valores de 5 para cima para o próximo dígito significativo e de 4 para baixo
    • Sempre arredondar para cima, etc.
    • O importante é manter consistência em todo o sistema (se a diferença for de 1 centavo por transação, em 10 milhões de transações isso vira US$ 100 mil)
  • Adie a conversão de moeda o máximo possível: converter moeda cedo demais pode causar perda de precisão
    • Adie a conversão cambial até depois que as agregações tiverem ocorrido na moeda local
  • Use representação inteira para tempo: é um pouco controverso, mas fortemente recomendado
    • Diferentes bibliotecas que fazem parsing de timestamps como objetos lidam com isso de formas diferentes
    • É melhor evitar essa dor de cabeça e usar inteiros
    • Unix timestamps ou datetimes inteiros baseados em UTC também funcionam perfeitamente
    • Quanto menos transformação de dados entre sistemas, melhor
    • Nota do Wasteman

      • Nem cheguei a mencionar bugs relacionados a horário de verão. Usar inteiros crescentes evita isso completamente
      • Se você insistir em datetime, use pelo menos UTC. É surpreendentemente comum ver empresas enormes usando timestamps que não estão em UTC

2 comentários

 
kandk 2024-07-12

Isso é realmente muito útil. Se você fizer conversões de tipo (decimal, float, double) e arredondamentos sem pensar, sem discutir isso no contexto do trabalho, pode dar um problemão.

 
GN⁺ 2024-07-12
Comentários do Hacker News
  • Ênfase na importância de usar uma metodologia de arredondamento consistente

    • Menciona a necessidade de gerenciar a estratégia de arredondamento por código de país no código de domínio do negócio
  • Recomendação de representar o tempo como inteiro

    • Recomenda usar timestamp Unix ou data/hora UTC baseada em inteiro
    • Válido para horários específicos no passado ou no futuro
    • Ex.: no caso de uma empresa com política de cancelamento de 48 horas, é possível calcular um timestamp futuro
    • Para itens como o horário de encerramento do ano fiscal por país, é necessário armazenar o fuso horário
  • Recomendação de usar banco de dados relacional em sistemas contábeis

    • Fornece propriedades ACID
    • Oferece tipos numéricos de precisão arbitrária e operações e modos de arredondamento validados
    • Permite cálculos e relatórios via SQL
    • Ao contratar especialistas em SQL, é possível produzir relatórios elegantes
    • Oferece alto desempenho e ferramentas de prevenção e recuperação de desastres
    • Compartilha experiência na construção de sistemas financeiros para multinacionais
  • Os principais objetivos de um sistema contábil são exatidão, auditabilidade e pontualidade

    • A consistência também é mencionada como um fator importante
    • Existem várias dimensões temporais, e é necessário fornecer uma visão consistente de acordo com cada uma delas
    • Ex.: se uma transação foi concluída, mas não liquidada, ela deve ser incluída apenas na contabilidade de front office
  • Opinião sobre a completude dos sistemas contábeis

    • Parte do princípio de que nem todas as transações são processadas no prazo
    • É necessário processar transações por meio de várias camadas de razão e ter um processo de reconciliação
  • Para negócios globais, recomenda-se usar pelo menos 8 casas decimais

    • Ênfase na necessidade de adiar a conversão cambial o máximo possível
    • A conversão cambial faz parte das obrigações legais e contábeis
  • Menção à importância da interface do usuário (UI)

    • Expressa frustração com a UI de softwares contábeis
    • Destaca a necessidade de soluções de UI melhores
  • Explicação da diferença entre processamento em lote e processamento em streaming

    • O design dos dois sistemas é completamente diferente
    • Há dificuldades para processar grandes volumes de dados já existentes
  • Compartilha experiência na construção de um sistema de faturas com TypeScript

    • Explica como evitar erros de arredondamento
    • Fornece link relacionado
  • Recomendação de usar classes da biblioteca padrão

    • Recomenda o uso de BigDecimal do Java e Decimal do Python
    • Destaca a necessidade de aplicar um padrão que mantenha a mesma escala ou armazene a escala
  • Explica as dificuldades de arredondamento e compartilhamento de dados

    • Problemas ao compartilhar dados com sistemas que só conseguem lidar com duas casas decimais
  • Compartilha experiência trabalhando com APIs dos 10 maiores bancos dos EUA

    • Menciona problemas de consistência na forma de armazenar taxas de juros
    • Reforça a importância da consistência
  • Recomendação de "Accounting Patterns", de Martin Fowler

    • Compartilha experiência na construção de um sistema de gerenciamento de eventos financeiros
    • Fornece link relacionado