Les ingénieurs ne peuvent pas se permettre des erreurs de startup lors de la conception d’un ledger
(news.alvaroduran.com)- Dans la fintech, où l’on manipule de l’argent réel, un écart de quelques centimes suffit à briser la confiance des utilisateurs, et une startup de trading qui enregistrait les transactions en partie simple a subi le problème des dancing cents
- Un ledger en partie simple ne conserve que les entrées et sorties d’argent, ce qui rend difficile de retracer l’origine des écarts, qu’ils viennent du change, du rounding to even du broker ou des frais FINRA TAF
- Un ledger en partie double considère que l’argent se déplace toujours d’un compte à un autre, et sépare Accounts, Entries et Transactions pour enregistrer à la fois la source et la destination
- En définissant clairement les états pending, discarded et posted des Entries ainsi que les conditions de publication des Transactions, on peut gérer plus sûrement les échecs partiels et les Entries de compensation
- Le ledger est à la fois l’interface des rapports comptables et le system of record qui garantit la cohérence de l’argent, ce qui accentue avec la montée en charge la tension entre disponibilité et forte cohérence
Quelques centimes ont suffi à faire s’effondrer la confiance des utilisateurs
- Une startup qui développait une plateforme de trading suivait le principe « make it work, make it right, make it fast » et n’a donc pas construit dès le départ un système comptable en partie double
- Juste après le lancement, les montants reconnus par le prestataire et ceux reconnus par le système interne différaient de quelques centimes
- En interne, ils appelaient cela les dancing cents
- Un utilisateur achetait pour 5 dollars d’actions Apple, mais si l’ordre apparaissait à 4,98 dollars, il contactait immédiatement le support
- Le vrai problème n’était pas le montant perdu, mais la confiance et la croissance
- Les utilisateurs mécontents ne recommandaient pas le service, ce qui freinait la croissance de la startup
- Le CEO a demandé au support client de compenser manuellement les quelques centimes lorsqu’une transaction erronée se produisait
- Ils ont même créé un bot Slack pour traiter cela
L’argent ne suit pas seulement le solde actuel, mais aussi la valeur future
- Un ledger est un système de suivi de l’argent
- L’argent ne se résume pas à un solde courant ; il sert aussi à représenter une valeur que l’on recevra ou que l’on devra plus tard
- Conceptuellement, l’argent est un actif futur
- Enregistrer uniquement les entrées et sorties, comme « l’utilisateur a payé 5 dollars » ou « l’utilisateur a payé 6 dollars », ne suffit pas à expliquer les flux financiers réels
- Un virement bancaire est lent à l’échelle d’Internet, et de nombreuses banques règlent les transferts le jour ouvré suivant
- Une fois le paiement terminé, on a la certitude que l’argent sera reçu à un moment donné
- Mais l’action doit être achetée immédiatement via le broker
- Il faut donc pouvoir représenter en même temps un montant pending qui sera réglé quelques jours plus tard et un montant qui part immédiatement vers le broker
- Dans un système en partie simple, un rollback est très difficile en cas d’erreur, et dans certains cas limites il est même impossible d’essayer
Pourquoi un ledger en partie simple empêche le debugging
- Un ledger en partie simple peut montrer les flux de fonds, mais il ne peut pas expliquer pourquoi ils se sont produits
- Pour retrouver la cause d’un mouvement d’argent précis, il fallait recoller des données issues de plusieurs modèles, et dans certains cas c’était tout simplement impossible
- Un ledger en partie double conserve à la fois ce qui s’est passé et pourquoi cela s’est produit
- Chaque mouvement d’argent se fait d’un compte vers un autre
- Chaque centime est enregistré avec son compte d’origine et son compte de destination
- Le problème des dancing cents était difficile à résoudre dans un système en partie simple
- Il était difficile de savoir si les quelques centimes manquants venaient du change
- Ils pouvaient venir du rounding to even mechanism du broker
- Ou des FINRA TAF fees prélevés en fin de journée
- Tant qu’on ne comprend pas comment le système fonctionne, il est difficile d’éliminer les bugs
Modèle de données du ledger : Accounts, Entries, Transactions
- Beaucoup d’ingénieurs, lorsqu’ils commencent à suivre de l’argent, placent le montant directement dans le modèle métier
- Par exemple en ajoutant un attribut price à Order ou une colonne amount à la table expenses
- C’est l’approche balance as property
- Cette méthode fonctionne rapidement au début, mais avec le temps les reportings deviennent complexes et lents, et le traitement des paiements comme l’analyse deviennent plus difficiles
- Si un batch de reporting nocturne prend plusieurs heures, cette approche peut en être la cause profonde
- Il vaut mieux traiter le ledger comme un modèle de données distinct à partir duquel toutes les transactions financières du système peuvent être dérivées
- Trois entités forment la structure de base
- Accounts : des réservoirs de valeur et la perspective depuis laquelle on observe l’évolution de cette valeur dans le temps
- Entries : les flux de fonds entre comptes, qui représentent toujours un échange de valeur
- Transactions : l’unité qui garantit que les Entries sont correctement appariées et traitées
États et immutabilité des Entries
- Les Entries peuvent avoir trois états : pending, discarded et posted
- Une Entry est toujours créée à l’état pending
- la valeur échangée
- la direction, credit ou debit
- les informations du compte référencé
- Représenter la direction du montant avec des valeurs positives et négatives est une erreur fréquente
- Les Entries sont immuables par défaut, mais une Entry pending peut être discarded pour permettre la création d’une Entry posted
- Une autre possibilité consiste à créer une reversal Entry pour annuler une Entry pending
- Mais l’approche par reversal Entry peut rendre l’historique du compte désordonné
- Avec l’état discarded, il suffit d’exclure les éléments où
discarded_atest renseigné lorsqu’on consulte les Entries courantes, sans perdre l’historique
- Dans un système en partie double, le total des credit Entries non discarded est égal au total des debit Entries non discarded
- Conceptuellement, cela signifie que la somme totale reste la même, quelle que soit la manière dont on déplace l’argent d’une poche à l’autre
- Quelques comptes spéciaux représentant le monde extérieur et consolidés dans le Profit and Loss statement peuvent exceptionnellement ne pas être équilibrés
Transactions et gestion des échecs partiels
- Les Entries sont créées par paires, et les Transactions garantissent que le processus se déroule comme prévu
- Une Transaction n’est postée que si les Entries associées sont dans l’état posted ou discarded et remplacées par des posted Entries
- Une Transaction ayant subi un échec partiel peut être annulée sémantiquement via des compensating Entries
- Cette approche s’accorde bien avec le Saga pattern
- Le pattern Saga échange l’atomicité contre la disponibilité
- Au lieu de transactions lentes verrouillant plusieurs tables, il découpe le processus en tâches individuelles plus petites avec des points de contrôle intermédiaires
- Entre-temps, d’autres transactions peuvent travailler, ce qui améliore le débit
Accounts et normal balance
- Du point de vue d’un seul Account, un ledger ressemble à un système en partie simple
- Un Account a une relation un-à-plusieurs avec plusieurs Entries
- Le solde total doit correspondre à l’agrégation des soldes individuels des Entries liées
- La manière de calculer le total varie selon la normal balance du compte
- En comptabilité, il faut éviter de lier le montant d’une Entry à un signe positif ou négatif
- Certains comptes ont normalement un net credit, d’autres un net debit
- Par exemple, un compte de trésorerie bancaire peut normalement être en net debit, mais devenir négatif en cas de découvert
- Une normal credit balance signifie qu’il est normal que le total des credit Entries liées soit supérieur au total des debit Entries
- Une normal debit balance signifie l’inverse
La tension entre système comptable et système d’ingénierie
- Deux systèmes aux exigences différentes coexistent dans un ledger
- Accounting system : l’interface du ledger vue de l’extérieur
- Engineering system : l’implémentation par laquelle le ledger se voit lui-même
- L’Accounting system expose des données agrégées selon plusieurs perspectives
- Reporting
- Financial ratios
- Business Intelligence
- L’Engineering system doit garantir la cohérence et l’exactitude des données
- Dans une fintech, le ledger joue le rôle de source of truth, comme le CRM pour l’équipe commerciale
- Si le passage à l’échelle d’un ledger est difficile, c’est parce que les exigences de ces deux systèmes diffèrent
- L’Accounting system exige une haute disponibilité et une faible latence
- L’Engineering system exige une forte cohérence et des validations schema-on-write
Ressources comptables utiles pour les développeurs
- An Engineer’s Guide to Double-Entry Bookkeeping : explique la comptabilité en partie double avec du code Python de base
- Double Entry Accounting For Developers : explication de la comptabilité en partie double pour développeurs dans Django Hordak
- Modern Treasury ledger series part I : premier article d’une série en six parties sur le passage à l’échelle d’un ledger
- Beancount, Martin Kleppmann, Modern Treasury Accounting for Developers : ressources pour développeurs qui expliquent la comptabilité sous des angles différents
- Peter Selinger accounting tutorial : tutoriel pour aller plus loin
- Uber, Square et Airbnb ont aussi publié leur manière d’implémenter un ledger en partie double dans leurs propres systèmes
1 commentaires
Commentaires sur Hacker News
J’aimerais qu’on aille dire ça aux clients de Synapse. Des millions de dollars ont disparu.
Les banques doivent rapprocher leurs comptes selon des règles strictes pour savoir où est parti l’argent, mais les fintechs placent généralement leur propre ledger au-dessus d’un ou de quelques comptes FBO sous-jacents où l’argent des clients est mutualisé, afin de suivre le solde de chaque client. Dans le cas de Synapse, le total des soldes clients dans son propre ledger était bien supérieur au solde réel des comptes FBO.
Beaucoup de gens soupçonnent une fraude, mais je parierais plutôt sur un ledger tout simplement chaotique et plein de bugs. Après avoir vu ce qui se passe en coulisses, je ne pense pas que je mettrais un jour de l’argent sur un compte de dépôt fintech ; mieux vaut utiliser une vraie banque. Même si une fintech affirme que les dépôts sont couverts par la FDIC, cela ne vous protège que si la banque sous-jacente fait faillite, pas si la fintech n’est plus capable de suivre votre argent.
Référence : https://www.forbes.com/sites/zennonkapron/2024/11/08/what-th...
Andreessen Horowitz y avait investi, et ils sont du genre à mener une guerre totale contre toute réglementation gouvernementale.
https://finance.yahoo.com/personal-finance/synapse-bankruptc...
Avant cela, j’avais prédit que la base de code était tellement en bazar qu’on ne serait pas capables de suivre correctement ce genre de choses, et je m’étais disputé avec mon manager, qui croyait que tout fonctionnait comme par magie. Quelques jours plus tard, j’ai reçu un e-mail annonçant le lancement d’un audit sur des écarts comptables.
JPMC a proposé en interne d’utiliser des cryptomonnaies pour gérer les flux de trésorerie de façon cohérente, mais je ne sais pas jusqu’où ils sont réellement allés.
https://lex.substack.com/p/podcast-what-really-happened-at-s...
J’ai contesté pendant un moment, mais ils ne l’ont jamais vraiment reconnu ni corrigé. Sans preuve, j’ai soupçonné qu’il pouvait s’agir d’un vol interne discret, mais l’incompétence est probablement l’explication la plus plausible.
Un ami qui travaillait comme administrateur système chez BoA m’a dit que certains logs devaient être conservés 7 ans, mais qu’ils les supprimaient tout simplement quand les disques commençaient à être pleins.
L’une des choses qu’il m’a fallu du temps pour comprendre quand j’ai commencé à travailler chez Google, c’est le fait de sacrifier de la fiabilité ou de l’exactitude au nom de la scalabilité.
Avant cela, j’avais construit des systèmes de facturation ou de petites applications web OLTP, et des questions comme la perte de données acceptable ou un taux d’erreur non nul ne m’étaient même jamais venues à l’esprit. Plus que le fait que, quand on traite des millions de requêtes par seconde, certaines échouent, c’est la différence d’attitude d’ingénierie qui m’a le plus choqué.
À cet instant précis, il y a peut-être quelques milliers de personnes qui ouvrent Gmail et pour qui ça ne se charge pas correctement, ou qui reçoivent une erreur 500. Personne n’en cherche la cause, parce que l’utilisateur va rafraîchir la page et continuer sa journée. À l’inverse, même si la durabilité du stockage affiche un chiffre impressionnant de 99,99999 % par an, avec 2 milliards de clients, 200 personnes vont passer une journée vraiment catastrophique.
Passer de l’habitude d’enquêter sur chaque erreur dans les logs à une approche où tout est toujours un peu cassé et où l’on évalue d’abord le coût avant de réparer quelque chose a été un vrai choc.
Une règle empirique que j’ai souvent vue à des échelles comparables consiste à viser une perte de données tous les 100 ans sur l’ensemble de l’infrastructure. En général, le coût augmente de bien moins de 10 %, mais il faut quelqu’un qui comprenne la combinatoire lors de la conception des algorithmes de placement des données.
Si vous devez comprendre ce domaine, l’article sur les copysets est un bon point de départ.
Quand j’ai présenté la solution en mentionnant ce taux d’erreur, on m’a demandé comment ces 200 000 utilisateurs Android pourraient récupérer leurs données. Il n’y avait aucun moyen de récupération, et la synchronisation des contacts allait simplement être cassée pour eux ; on m’a donc dit de revoir la conception.
Les chiffres eux-mêmes rendent humble. Il y a clairement des domaines où 99,99 % suffit, mais il y en a tout autant où ce n’est pas suffisant.
Dans ce genre de cas, il est utile de recruter les bonnes personnes dès le départ. Il ne faut pas embaucher une armée de spécialistes de LeetCode sans leur demander s’ils sont capables de construire réellement ce que l’on veut construire, au-delà de leur capacité à ressortir de tête des structures de données et des algorithmes.
Si les gens savent comment construire ce dont il est question, il n’est pas nécessaire de sacrifier la croissance, et les choses sont bien faites dès le départ.
Parfois, il faut des ingénieurs qui viennent d’autres formations, comme la comptabilité, la finance ou la biologie. La partie la plus importante de ma carrière a été de comprendre en profondeur le secteur pour lequel je construisais, et de connaître des experts du domaine capables de poser les questions vraiment importantes. C’est ça, la résolution de problèmes et l’ingénierie ; le reste, c’est de la programmation/du code.
J’ai passé la majeure partie de ma carrière à l’intersection de la technologie et de la finance, principalement autour de la conformité en matière de taxes de vente et d’utilisation. Avec le recul, je n’avais pas pleinement réalisé à quel point les comptables, contrôleurs de gestion et juristes m’avaient influencé.
Récemment, j’ai conseillé une startup assez ancienne sur son système de grand livre, et j’ai été horrifié de voir ce que donne la construction de systèmes comptables par des ingénieurs sans bagage en finance ou en comptabilité.
Il n’est pas nécessaire de trouver un comptable-ingénieur magique. Il suffit de faire asseoir un vrai comptable à côté de l’équipe d’ingénierie pendant la conception. Après avoir terminé la refonte complète, je l’ai fait relire intégralement par un ami CPA ; il a trouvé des trous dans quelques scénarios, mais dans l’ensemble c’était correct.
L’argent est un problème d’ingénierie difficile, parce qu’il charrie toute l’absurdité humaine qui l’entoure.
Cela rappelle à quel point la connaissance métier est importante dans le leadership d’ingénierie. Si vous travaillez dans une entreprise financière, vous devez comprendre un minimum la finance pour prendre les bonnes décisions techniques et faire les bons compromis ; il en va de même pour le journalisme ou le commerce.
Les organisations performantes dans lesquelles j’ai travaillé incluaient toujours des questions non techniques propres au domaine dans les entretiens des équipes techniques. À l’inverse, certaines équipes techniquement très fortes se sont retrouvées en difficulté faute de compréhension métier.
Partout où je regarde, on semble préférer très largement quelqu’un qui a deux fois plus d’expérience en ingénierie logicielle que moi, même sans aucune connaissance métier, plutôt que mon profil avec une expérience en comptabilité et une expérience relativement plus courte en développement. Je me demande comment exploiter cela efficacement.
La finance est technique, le génie mécanique est technique, et la gestion du sport comme la sociologie comportent aussi de grands aspects techniques. En élargissant notre vision de ce qu’est une compétence technique, on développe l’humilité nécessaire pour collaborer avec plusieurs domaines.
C’est au PM de vérifier avec l’ingénierie que les exigences sont correctes et que le produit construit y répond. Dans un environnement agile, ces discussions et validations ont lieu à chaque sprint, donc il est difficile qu’un problème passe trop longtemps entre les mailles du filet.
S’il n’y a pas de PM, alors oui, l’équipe d’ingénierie a besoin d’une connaissance métier profonde ; sinon, ce n’est pas la responsabilité de l’ingénierie. C’est celle de l’organisation produit.
Pour raconter une vieille histoire, je n’ai jamais construit de système de comptabilité en partie double, mais il y a des décennies, dans une startup Internet/télécoms dont le chiffre d’affaires a atteint huit chiffres, j’ai construit un système de facturation.
En tant que jeune développeur, je n’y connaissais pas grand-chose et, par hasard, je me suis retrouvé dès le premier jour à écrire la logique de facturation. Pour le meilleur ou pour le pire, je l’ai implémentée à deux endroits : une page web de facturation côté consommateur, et un processus backend séparé qui générait les factures et effectuait les paiements par carte bancaire.
Les maintenir synchronisés s’est révélé étonnamment difficile. Nous itérions sans cesse en brûlant du capital pour trouver l’adéquation au marché, et de nouveaux produits et services, de nouvelles remises et méthodes de tarification, de la facturation à l’usage, de la facturation mensuelle, les X premières utilisations gratuites, des payeurs principaux/sous-comptes pour les comptes entreprise, des centres de coûts définis par l’utilisateur, puis la ventilation des taxes et des centimes sur ces centres de coûts, n’arrêtaient pas de s’ajouter. À chaque fois, de nouveaux plis et exceptions apparaissaient, et les chiffres ne correspondaient plus entre les deux écrans/méthodes.
Comme j’étais responsable de la facturation, je passais plusieurs jours chaque mois à examiner toutes les factures à la main, pour vérifier une dernière fois que les chiffres concordaient avant les prélèvements par carte bancaire et l’envoi des factures papier. Je trouvais toujours, ou souvent, un nouveau problème affectant un client ou un petit nombre de clients, puis je corrigeais le code avant la facturation réelle. J’ai toujours été mal à l’aise à l’idée de tout laisser partir sans revérification manuelle.
J’ai envisagé de refactorer la logique de facturation en un seul endroit pour éliminer les divergences et les validations croisées manuelles, mais après mûre réflexion, j’ai réalisé qu’une base de code unique me mettait mal à l’aise, et que deux bases de code m’aidaient au contraire à attraper mes erreurs. Par la suite, j’ai progressivement facilité l’exécution automatique et la validation croisée entre les deux implémentations.
Le code de facturation était un peu trop désordonné pour que j’en sois fier, mais je suis très fier de l’exactitude de la facturation, du faible nombre de réclamations et des erreurs vertigineuses évitées pendant plusieurs années. Je me sens un peu coupable de la complexité laissée à mes successeurs, mais aujourd’hui encore, je ne le regrette pas vraiment.
Après cette expérience, j’ai toujours bien compris la motivation de la comptabilité en partie double. J’avais en quelque sorte réinventé, de façon bricolée, un code de facturation à double logique pour m’empêcher de commettre des erreurs au détriment des clients.
Dans une ancienne entreprise, l’équipe data que j’ai fini par diriger avait la fâcheuse habitude de « perdre » de l’argent. Ce n’était pas que de l’argent réel disparaissait pendant un transfert ailleurs ; c’était plutôt que des enregistrements qui auraient dû être facturés aux clients disparaissaient.
Quand nous ne perdions pas de chiffre d’affaires, nous faisions de la double facturation, et ce genre de choses continuait. Il a fallu 3 ans de travail difficile pour regagner la confiance de la direction.
Il n’y avait même pas de tests ? Si on perd de l’argent à chaque transaction, au point de donner l’exemple où « à chaque achat de 5 dollars, il restait 4,98 dollars dans le journal des transactions », le problème est bien plus grave que l’absence de comptabilité en partie double.
Qui construit un tel système financier et considère ça comme normal ? La rémunération est un problème, mais avec un service pareil, il faut fuir aussi vite que possible.
Ils faisaient des blagues du genre « centimes qui dansent » et l’ont fait parce qu’ils savaient qu’ils n’auraient pas à assumer de conséquences significatives. Ils ont avancé vite, cassé quelque chose — quelque chose lié à l’argent — puis en ont ri.
Et maintenant, ils essaient d’expliquer aux autres qu’ils auraient une autorité morale et technique justement parce qu’ils ont pris délibérément cette décision. C’est une absurdité incroyablement arrogante typique de la culture startup/VC.
Bien sûr, cela peut donner des indices pour trouver le bug, mais écrire des tests de base aurait fait la même chose.
Je ne comprends pas pourquoi l’auteur présente négativement l’adage « make it work, make it right, make it fast ». Il a probablement mal compris où se place « make it fast ».
« Make it right » est la deuxième étape, et tant que le système ne fonctionne pas correctement, le travail doit s’arrêter là. Le système doit fonctionner de manière saine. « Make it fast », c’est-à-dire l’optimisation, ne doit commencer qu’une fois les problèmes d’exactitude et de solidité entièrement résolus.
Cela n’a rien à voir avec la vitesse de livraison ou le fait de travailler vite ; cela signifie qu’il faut repousser l’optimisation à la dernière étape.
Cela dit, si ce que l’auteur voulait dire est qu’une chose peut vaguement « fonctionner » tout en étant tellement éloignée du « correct » qu’on ne pourra pas la reprendre plus tard, et qu’il faut donc la construire « correctement » dès le départ avant même qu’elle commence à peine à fonctionner, alors je comprends.
Je suis du même avis, et j’ai déjà participé à l’audit de systèmes fintech. Les auditeurs devaient tout exporter dans des feuilles Excel avant d’approuver les comptes, puis réconcilier les chiffres. Cela coûtait beaucoup de temps et d’argent, et j’imagine que, trois ans plus tard, lors d’un événement de liquidité, cela a dû représenter au moins 0,1 licorne de différence.
Dans une startup qui avance vite, on lance en pratique un MVP aussi rapidement que possible. Comme il faut construire une base de clients, des finances, etc., on reste bloqué à l’étape « make it work ».
Un meilleur adage aurait été celui de Facebook, « move fast and break things ». Mais cela ne fonctionne que lorsqu’on peut réparer ensuite. Par exemple, on ne ferait pas ça en construisant un avion.
Vu le contexte, le malentendu mentionné dans la première phrase paraît le plus plausible, puisqu’il est immédiatement suivi d’une discussion sur la pression temporelle subie par les startups.
La plupart des commentaires ici répètent exactement ce que l’article critique. On voit d’innombrables longues argumentations en défense de la comptabilité en partie simple.
La comptabilité en partie simple est peut-être plus facile et plus répandue, mais il est parfois judicieux de simplement suivre des systèmes et abstractions développés au fil des siècles.
À moins d’avoir absolument besoin d’autre chose, mieux vaut utiliser la comptabilité en partie double. Cela peut sembler contre-intuitif aux instincts de programmeur, mais le jour où il faudra faire venir un vrai comptable pour démêler les incohérences, vous en serez reconnaissant.
À ce sujet, quelqu’un connaît-il de bonnes ressources pour les programmeurs travaillant dans les paiements ou des domaines voisins ? Quelque chose comme « la comptabilité pour les programmeurs ».
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...
Si l’on vous disait que vous utilisez un système de base de données qui perd 1 % des données toutes les 10 transactions, pourriez-vous prendre au sérieux ce genre de conseils dans un blog d’ingénierie ? Ce texte donne moins l’impression d’une introduction conceptuelle que d’une publicité promotionnelle soigneusement emballée pour une personne ou un groupe précis.
Pour voir quelles conséquences réelles ce genre d’attitude négligente envers les logiciels qui déplacent de l’argent peut avoir sur de vraies personnes, regardez le scandale du Post Office.
https://en.wikipedia.org/wiki/British_Post_Office_scandal
Tout ce qui déplace de l’argent doit être traité avec le plus grand sérieux, et il faut connaître autant d’échecs historiques que possible.
https://en.wikipedia.org/wiki/Mr_Bates_vs_The_Post_Office