Engenheiros não podem cometer erros de startup ao construir um Ledger
(news.alvaroduran.com)- Em fintechs que lidam com dinheiro real, até alguns centavos de diferença abalam a confiança do usuário, e uma startup de negociação de ações que registrava transações com um modelo de entrada única sofreu com o problema dos dancing cents
- Um ledger de entrada única registra apenas entradas e saídas de dinheiro, o que dificulta rastrear a causa de diferenças criadas por câmbio, rounding to even do corretor ou taxas FINRA TAF
- Um ledger de dupla entrada parte do princípio de que o dinheiro sempre se move de uma conta para outra e separa Accounts, Entries e Transactions para registrar juntos a origem e o destino
- Se os estados pending, discarded e posted de Entries e as condições de publicação de Transactions forem definidos com clareza, fica mais seguro lidar com falhas parciais e compensating Entries
- O Ledger é tanto uma interface para relatórios contábeis quanto o system of record que preserva a consistência do dinheiro, por isso a tensão entre disponibilidade e consistência forte aumenta conforme ele escala
Alguns centavos de diferença destruíram a confiança do usuário
- Uma startup que estava criando uma plataforma de negociação de ações seguiu o princípio “make it work, make it right, make it fast” e não construiu desde o início um sistema contábil de dupla entrada
- Logo após o lançamento, os valores reconhecidos pelo fornecedor e pelo sistema interno começaram a divergir em alguns centavos
- Internamente, chamavam isso de dancing cents
- O usuário comprava 5 dólares em ações da Apple, mas via a ordem aparecer como 4,98 dólares e imediatamente entrava em contato com o suporte
- A essência do problema não estava no valor perdido, mas na confiança e no crescimento
- Usuários irritados não recomendavam o serviço, e o crescimento da startup travava
- O CEO orientou o suporte ao cliente a compensar manualmente os centavos quando ocorressem transações incorretas
- Até criaram um bot no Slack para lidar com isso
Dinheiro rastreia não apenas o saldo atual, mas também valor futuro
- O Ledger é um sistema para rastrear dinheiro
- Dinheiro não representa apenas o saldo atual; ele também é necessário para expressar valor a receber ou a pagar no futuro
- Conceitualmente, dinheiro é um ativo futuro
- Registrar apenas entradas e saídas, como “o usuário pagou 5 dólares” ou “o usuário pagou 6 dólares”, não basta para explicar o fluxo financeiro real
- Transferências bancárias são lentas para os padrões da internet, e muitos bancos liquidam a transferência apenas no próximo dia útil
- Quando o pagamento é concluído, surge a certeza de que o dinheiro será recebido em algum momento
- Mas a ação precisa ser comprada agora por meio da corretora
- É preciso representar ao mesmo tempo o valor pending que será liquidado dias depois e o valor que sai imediatamente para a corretora
- Em um modelo de entrada única, fazer rollback quando ocorre um erro é muito difícil e, em alguns casos extremos, nem sequer é possível tentar
Por que um ledger de entrada única impede o debugging
- Um ledger de entrada única pode mostrar o fluxo de fundos, mas não consegue explicar por que ele aconteceu
- Para descobrir a causa de uma movimentação específica, era preciso juntar dados de vários modelos e, em alguns casos, nem isso era possível
- Um ledger de dupla entrada registra ao mesmo tempo o que aconteceu e por que aconteceu
- Toda movimentação de dinheiro ocorre de uma conta para outra
- Cada centavo fica registrado com a conta de origem e a conta de destino
- O problema dos dancing cents era difícil de resolver em um sistema de entrada única
- Era difícil saber se os centavos desaparecidos vinham de câmbio
- Podiam vir do rounding to even mechanism da corretora
- Ou das FINRA TAF fees cobradas no fim do dia
- Se você não consegue entender como o sistema funciona, também fica difícil eliminar bugs
Modelo de dados do Ledger: Accounts, Entries, Transactions
- Muitos engenheiros, ao rastrear dinheiro pela primeira vez, colocam o valor monetário dentro do modelo de domínio
- Como adicionar uma propriedade
pricea um Order ou uma colunaamountà tabela de despesas - Essa é a abordagem balance as property
- Como adicionar uma propriedade
- Essa abordagem funciona rápido no início, mas com o tempo relatórios ficam complexos e lentos, e também se tornam mais difíceis o processamento de pagamentos e a análise
- Se um job noturno de relatórios leva várias horas, essa abordagem pode ser a causa raiz
- É melhor tratar o Ledger como um modelo de dados separado do qual todas as transações financeiras do sistema possam ser derivadas
- Três entidades formam a estrutura básica
- Accounts: buckets de valor e também a perspectiva pela qual se observa como o valor muda ao longo do tempo
- Entries: fluxos de fundos entre contas, sempre representando uma troca de valor
- Transactions: a unidade que garante que as Entries sejam corretamente pareadas e processadas
Estados e imutabilidade de Entries
- Entries podem ter três estados: pending, discarded e posted
- Uma Entry sempre é criada no estado pending
- O valor trocado
- A direção, credit ou debit
- As informações da account referenciada
- Representar a direção do valor com números positivos e negativos é um erro comum
- Entries são imutáveis por padrão, mas uma pending Entry pode ser discarded para criar uma posted Entry
- Uma alternativa seria criar uma reversal Entry para desfazer uma pending Entry
- Mas a abordagem com reversal Entry pode deixar o histórico da conta confuso
- Com o estado discarded, ao olhar as Entries atuais basta excluir os itens com
discarded_atdefinido, sem perder o histórico
- Em um sistema de dupla entrada, o total de credit Entries não descartadas é igual ao total de debit Entries não descartadas
- Conceitualmente, isso significa que o valor total permanece o mesmo, não importa como o dinheiro seja movido entre bolsos
- Algumas contas especiais que representam o mundo externo e são consolidadas no Profit and Loss statement excepcionalmente podem não fechar em equilíbrio
Transactions e tratamento de falhas parciais
- Entries são criadas em pares, e Transactions garantem que esse processo avance conforme o pretendido
- Uma Transaction só é posted quando as Entries vinculadas estão em estado posted ou discarded e foram substituídas por posted Entries
- Uma Transaction com falha parcial pode ser revertida semanticamente com compensating Entries
- Essa abordagem combina bem com o Saga pattern
- Saga troca atomicidade por disponibilidade
- Em vez de transações lentas que bloqueiam várias tabelas, ela divide o processo em tarefas menores e checkpoints intermediários
- Nesse intervalo, outras transações podem avançar, o que melhora o throughput
Accounts e normal balance
- Do ponto de vista de uma única Account, o Ledger se parece com um sistema de entrada única
- Uma Account tem uma relação um-para-muitos com várias Entries
- O saldo total precisa bater com a agregação dos saldos individuais das Entries vinculadas
- A forma de calcular o total muda conforme o normal balance da Account
- Vincular sinal positivo ou negativo ao valor da Entry deve ser evitado do ponto de vista contábil
- Algumas contas têm como normal um net credit, e outras um net debit
- Por exemplo, uma conta bancária de caixa pode normalmente ter net debit, mas pode ficar negativa se houver saque a descoberto
- normal credit balance significa que o total de credit Entries vinculadas é maior que o total de debit Entries
- normal debit balance significa o contrário
A tensão entre o sistema contábil e o sistema de engenharia
- No Ledger convivem dois sistemas com exigências diferentes
- Accounting system: a interface externa do Ledger
- Engineering system: a implementação pela qual o Ledger enxerga a si próprio
- O Accounting system expõe dados agregados a partir de várias perspectivas
- Reporting
- Financial ratios
- Business Intelligence
- O Engineering system precisa garantir consistência e precisão dos dados
- Em uma fintech, o Ledger exerce o papel de source of truth, como um CRM para a equipe comercial
- Escalar um Ledger é difícil porque as exigências dos dois sistemas são diferentes
- O Accounting system exige alta disponibilidade e baixa latência
- O Engineering system exige consistência forte e validações schema-on-write
Materiais de contabilidade para desenvolvedores
- An Engineer’s Guide to Double-Entry Bookkeeping: explica contabilidade de dupla entrada com código básico em Python
- Double Entry Accounting For Developers: explicação de contabilidade de dupla entrada para desenvolvedores no Django Hordak
- Modern Treasury ledger series part I: primeiro texto de uma série de 6 partes sobre escalabilidade de Ledger
- Beancount, Martin Kleppmann, Modern Treasury Accounting for Developers: materiais para desenvolvedores que explicam contabilidade sob ângulos diferentes
- Peter Selinger accounting tutorial: tutorial para estudar o tema com mais profundidade
- Uber, Square e Airbnb também divulgaram como implementaram um Ledger de dupla entrada em seus próprios sistemas
1 comentários
Opiniões no Hacker News
Gostaria que dissessem isso também aos clientes da Synapse. Milhões de dólares desapareceram
Bancos precisam reconciliar seus livros segundo regras rigorosas para saber para onde o dinheiro foi, mas fintechs normalmente colocam seu próprio ledger por cima de uma ou algumas contas FBO básicas onde o dinheiro dos clientes fica agrupado, para rastrear o saldo de cada cliente. No caso da Synapse, a soma dos saldos dos clientes no seu próprio ledger era muito maior do que o saldo real das contas FBO
Muita gente suspeita de fraude, mas eu apostaria que era simplesmente um ledger bagunçado e cheio de bugs. Depois de ver por dentro, acho que jamais colocaria dinheiro em uma conta de depósito de fintech; é melhor usar um banco de verdade. Mesmo que a fintech divulgue que os depósitos são segurados pelo FDIC, isso só protege você se o banco subjacente quebrar, não se a fintech deixar de conseguir rastrear o seu dinheiro
Referência: https://www.forbes.com/sites/zennonkapron/2024/11/08/what-th...
A Andreessen Horowitz investiu nisso, e eles estão do lado de quem trava uma guerra total contra toda regulamentação governamental
https://finance.yahoo.com/personal-finance/synapse-bankruptc...
Antes disso, eu já previa que a base de código era tão bagunçada que não conseguiria rastrear essas coisas corretamente, e discuti com um chefe que acreditava que tudo funcionava como mágica. Alguns dias depois, recebi um e-mail dizendo que uma auditoria sobre discrepâncias contábeis começaria
O JPMC propôs internamente usar criptomoeda para gerenciar fluxos de caixa de forma consistente, mas não sei até onde isso realmente chegou
https://lex.substack.com/p/podcast-what-really-happened-at-s...
Reclamei por um tempo, mas eles nem reconheceram direito nem corrigiram. Sem provas, desconfiei que pudesse ser algum furto interno sutil, mas incompetência parece uma explicação mais plausível
Um amigo que trabalhou como administrador de sistemas no BoA disse que certos logs precisavam ser mantidos por 7 anos, mas, quando o disco começava a encher, eles simplesmente os apagavam
Uma das coisas que levei algum tempo para entender quando comecei a trabalhar no Google foi o trade-off entre confiabilidade ou precisão e escalabilidade
Antes, eu havia construído sistemas de cobrança ou pequenas aplicações web OLTP, e nunca tinha sequer pensado em perguntas como perda de dados aceitável ou uma taxa de erro diferente de zero. Mais do que o fato de que, ao processar milhões de requisições por segundo, algumas falham, o choque maior foi a diferença de atitude de engenharia
Mesmo neste exato momento, pode haver alguns milhares de pessoas que abriram o Gmail e ele não carregou corretamente ou receberam um erro 500. Ninguém rastreia a causa, porque o usuário vai atualizar a página e seguir o dia. Por outro lado, mesmo que a durabilidade de armazenamento de 99,99999% ao ano seja um número impressionante, com 2 bilhões de clientes, 200 pessoas terão um dia realmente péssimo
A transição do hábito de investigar todo erro nos logs para um modo em que tudo está sempre um pouco quebrado e primeiro se calcula o custo antes de consertar algo foi bastante chocante
Uma regra prática que vi bastante em escala semelhante é mirar em 1 caso de perda de dados a cada 100 anos considerando toda a infraestrutura. Em geral, o custo aumenta bem menos que 10%, mas é preciso alguém que entenda combinatória ao projetar o algoritmo de distribuição de dados
Se você precisa entender esse assunto, o artigo sobre copysets é um bom ponto de partida
Ao apresentar a solução e mencionar essa taxa de erro, perguntaram como aqueles 200 mil usuários de Android poderiam se recuperar. Como não havia forma de recuperação e a sincronização de contatos simplesmente quebraria, disseram para eu redesenhar a solução
O número em si me deixou mais humilde. Certamente há áreas em que 99,99% é suficiente, mas há tantas outras em que não é
Esse tipo de coisa ajuda quando se contrata a pessoa certa desde o início. Não dá para contratar um monte de especialistas em LeetCode e deixar de perguntar se eles conseguem construir aquilo que você realmente quer construir, além da capacidade de evocar estruturas de dados e algoritmos de cabeça
Se as pessoas sabem como aquilo deve ser construído, não é preciso sacrificar o crescimento, e as coisas já nascem feitas corretamente
Às vezes é preciso ter engenheiros com outras formações, como contabilidade, finanças ou biologia. A parte mais importante da minha carreira foi entender profundamente o setor para o qual eu estava construindo e conhecer especialistas daquela área que conseguissem fazer as perguntas que realmente importam. Isso é resolução de problemas e engenharia; o resto é programação/codificação
Passei a maior parte da carreira na interseção entre tecnologia e finanças, principalmente em conformidade de impostos sobre vendas e uso. No processo, não percebi plenamente o quanto contadores, controllers e advogados me influenciaram
Recentemente assessorei o sistema de razão contábil de uma startup já bastante antiga e fiquei horrorizado ao ver como fica quando engenheiros sem formação em finanças ou contabilidade constroem um sistema contábil
Não é preciso encontrar um contador-engenheiro mágico. Basta colocar um contador de verdade ao lado da equipe de engenharia durante o processo de projeto. Depois de terminar o desenho da reformulação completa, pedi a um amigo CPA que fizesse uma revisão completa; ele encontrou falhas em alguns cenários, mas no geral estava tudo bem
Dinheiro é um problema difícil de engenharia. Porque o dinheiro traz consigo todo tipo de esquisitice humana ao seu redor
Isso reforça a importância do conhecimento de domínio na liderança de engenharia. Se você trabalha em uma empresa financeira, precisa entender finanças em algum nível para tomar as decisões técnicas e os trade-offs corretos; o mesmo vale para jornalismo ou comércio
As organizações bem-sucedidas em que trabalhei sempre incluíam perguntas não técnicas específicas do domínio nas entrevistas da equipe técnica. Em contrapartida, algumas equipes tecnicamente excelentes patinavam por falta de percepção sobre o domínio
Para onde quer que eu olhe, parecem preferir muito mais alguém com o dobro de experiência em engenharia de software, mesmo sem nenhum conhecimento de domínio, do que eu, com experiência em contabilidade e uma carreira relativamente curta como engenheiro de software. Fico me perguntando se há uma forma de aproveitar isso de maneira eficaz
Finanças também são técnicas, engenharia mecânica também é técnica, e há um grande componente técnico em gestão esportiva ou sociologia. Quando se adota uma visão mais ampla do que é competência técnica, surge a humildade necessária para colaborar em vários domínios
O trabalho do PM é verificar, junto com a engenharia, se os requisitos estão corretos e se o produto construído atende a esses requisitos. Em um ambiente ágil, essas conversas e validações acontecem a cada sprint, então é difícil algo passar muito tempo sem ser filtrado
Se não houver PM, então a equipe de engenharia precisa de conhecimento profundo de domínio; caso contrário, isso não é responsabilidade da engenharia. É responsabilidade da organização de produto
Contando uma história antiga: nunca construí um sistema de partidas dobradas, mas décadas atrás construí um sistema de cobrança em uma startup de internet/telecom que cresceu até uma receita de oito dígitos
Como desenvolvedor jovem, eu não sabia muito e, por acaso, acabei fazendo a lógica de cobrança desde o primeiro dia; para o bem ou para o mal, implementei em dois lugares do sistema. Uma página web de cobrança voltada ao consumidor e um processo de backend separado que gerava faturas e processava pagamentos com cartão de crédito
Manter os dois alinhados era surpreendentemente difícil. Queimávamos capital e iterávamos continuamente para encontrar tração no mercado, e recursos continuavam sendo adicionados: novos produtos e serviços, novos tipos de desconto e precificação, cobrança por uso, cobrança mensal, as primeiras X vezes grátis, pagador principal/subcontas em contas corporativas, centros de custo definidos pelo usuário, alocação de impostos e rateios de centavos nesses centros de custo. A cada vez surgiam novas rugosidades e exceções, e os números nas duas telas/métodos não batiam
Como eu era o responsável pela cobrança, todos os meses eu passava alguns dias revisando manualmente todas as faturas, fazendo uma checagem final para ver se os números batiam antes de processar os cartões de crédito e enviar as faturas em papel. Sempre, ou com frequência, eu encontrava um problema novo que afetava um ou alguns poucos clientes, e corrigia o código antes da cobrança real. Eu sempre ficava inseguro em liberar tudo sem reconferir manualmente
Pensei em refatorar a lógica de cobrança para um único lugar, a fim de eliminar as divergências e a validação cruzada manual, mas, depois de muito refletir, percebi que uma única base de código me deixava desconfortável e que, na verdade, duas bases de código ajudavam a capturar meus erros. Depois disso, fui tornando cada vez mais fácil executar automaticamente e fazer validação cruzada entre as duas implementações
O código de cobrança era meio bagunçado para se orgulhar, mas eu tinha muito orgulho da precisão da cobrança, da escassez de reclamações e dos erros assustadores evitados ao longo de vários anos. Tenho um pouco de culpa pela complexidade que deixei para meus sucessores, mas até hoje não me arrependo muito
Depois dessa experiência, sempre entendi bem a motivação por trás da contabilidade de partidas dobradas. De certa forma, para evitar que meus erros prejudicassem clientes, eu reinventei de maneira tosca um código de cobrança com lógica dupla
Em uma empresa anterior, a equipe de dados que passei a liderar tinha o infeliz hábito de “perder” dinheiro. Não era que dinheiro real desaparecesse enquanto se movia de um lugar para outro; eram registros do que deveria ser cobrado dos clientes que sumiam
Quando não estávamos perdendo receita, acabávamos fazendo cobranças duplicadas, e esse tipo de coisa continuou acontecendo. Foram necessários 3 anos de trabalho duro para reconquistar a confiança da diretoria
Nem sequer havia testes? Se eles estavam perdendo dinheiro em todas as transações a ponto de dar o exemplo de que “a cada compra de US$ 5, o log de transações ficava com US$ 4,98”, o problema é muito maior do que a ausência de contabilidade por partidas dobradas.
Quem constrói um sistema financeiro assim e acha normal? A compensação também é um problema, mas, diante de um serviço desses, o melhor é fugir o quanto antes.
Faziam piadas como “centavos dançantes” e fizeram isso porque sabiam que não precisariam arcar com consequências significativas. Moveram-se rápido, quebraram algo — algo relacionado a dinheiro — e deram risada.
Agora querem dar lição nas pessoas como se tivessem autoridade moral e técnica justamente por terem tomado essas decisões de propósito. É uma bobagem incrivelmente arrogante da cultura de startups e VCs.
Claro, poderia dar pistas para encontrar o bug, mas escrever testes básicos também teria feito isso.
Não entendo por que o autor usa de forma negativa o ditado “make it work, make it right, make it fast”. Talvez tenha entendido errado onde entra o “make it fast”.
“Make it right” é a segunda etapa, e o trabalho deveria parar ali até que o sistema funcione corretamente. O sistema precisa operar de forma saudável. “Make it fast”, ou seja, a otimização, só começa depois que os problemas de correção e solidez foram completamente resolvidos.
Isso não tem a ver com velocidade de entrega nem com trabalhar rápido; significa deixar a otimização para a última etapa.
Dito isso, se o ponto que o autor queria fazer é que, mesmo que algo “funcione” de forma aproximada, pode estar tão longe de estar “correto” que não dá para voltar e consertar depois, e que é preciso fazê-lo “certo” desde o início, antes mesmo de começar a funcionar minimamente, então faz sentido.
Concordo, e já participei de auditoria de sistemas fintech. Antes de aprovar os livros, os auditores precisavam baixar tudo para planilhas do Excel e reconciliar os números. Isso consumiu muito tempo e dinheiro, e imagino que tenha feito uma diferença de pelo menos 0,1 unicórnio em um evento de liquidez três anos depois.
Em startups que se movem rápido, lança-se um MVP de fato o mais rápido possível. Como é preciso construir base de clientes, finanças etc., a equipe acaba parando na etapa “make it work”.
Um ditado melhor teria sido o do Facebook, “move fast and break things”. Só que isso só funciona quando é possível consertar depois. Por exemplo, se você estiver construindo uma aeronave, não faria isso.
Pelo contexto, o mal-entendido mencionado na primeira frase parece o mais provável, já que logo depois o texto fala da pressão de tempo sofrida por startups.
A maioria dos comentários aqui está repetindo exatamente o que o texto critica. Vejo inúmeras discussões longas defendendo a contabilidade por partidas simples.
A contabilidade por partidas simples pode ser mais fácil e mais generalizada, mas às vezes é uma boa ideia simplesmente seguir sistemas e abstrações desenvolvidos ao longo de séculos.
A menos que você realmente precise de outra coisa, é melhor usar contabilidade por partidas dobradas. Isso pode incomodar o instinto de programador, mas, no momento em que você precisar chamar um contador de verdade para resolver inconsistências, vai agradecer.
A propósito, alguém conhece bons materiais para programadores que trabalham com pagamentos ou áreas próximas? Algo como “contabilidade para programadores”.
https://martin.kleppmann.com/2011/03/07/accounting-for-compu...
https://www.moderntreasury.com/journal/accounting-for-develo...
[0]: https://www.winstoncooke.com/blog/a-basic-introduction-to-ac...
Se alguém dissesse que está usando um sistema de banco de dados em que 1% dos dados desaparece a cada 10 transações, você levaria a sério conselhos desse tipo em um blog de engenharia? Este texto parece menos uma introdução de conceitos e mais um anúncio promocional, embalado de forma sóbria, para uma pessoa ou grupo específico.
Para ver que consequências esse tipo de atitude negligente em relação a software que movimenta dinheiro pode ter para pessoas reais, basta olhar para o escândalo dos Correios britânicos.
https://en.wikipedia.org/wiki/British_Post_Office_scandal
Tudo que movimenta dinheiro deve ser tratado com a máxima seriedade, e é preciso conhecer o maior número possível de fracassos históricos.
https://en.wikipedia.org/wiki/Mr_Bates_vs_The_Post_Office