Engineering-Prinzipien für den Aufbau von Finanzsystemen
(substack.wasteman.codes)- Die Buchhaltung hat sich in den vergangenen mehreren hundert Jahren kaum grundlegend verändert
- Trotzdem gibt es viel Verwirrung darüber, wie man Software für Finanzsysteme richtig baut
- Dieser Artikel teilt Erkenntnisse aus Erfahrungen beim Aufbau von Finanzsystemen in Großunternehmen
- Der Fokus liegt auf dem Aufbau von Buchhaltungssystemen, aber diese Prinzipien gelten auch für allgemeinere Finanzsysteme
Grundlegende Finanzbegriffe
- Hauptbuch (General Ledger, GL): Das zentrale Buchhaltungsregister eines Unternehmens, das alle Finanztransaktionen für einen bestimmten Zeitraum zusammenfasst. Man kann es als Aggregation der zugehörigen Nebenbücher verstehen
- Nebenbuch (Sub-ledger): Enthält Details zu allen einzelnen Transaktionen, die zu einem bestimmten GL gehören. Die Datensätze eines Nebenbuchs enthalten deutlich feiner granulierte Daten als das Hauptbuch (z. B. ein bestimmter Kunde, ein bestimmter Posten einer Bestellung usw.). Der Datenunterschied zwischen Nebenbuch und GL hängt von der Art des Geschäfts und der bearbeiteten Datenmenge ab. Manche kleineren Unternehmen können ohne Nebenbuch arbeiten, aber in kleineren Maßstäben ist maßgeschneiderte Software eher selten nötig
- Finanzaufzeichnungen (Financial Record): Sammelbegriff für Hauptbuch und Nebenbücher
- Wesentlichkeit (Material): Gibt an, ob eine Verzerrung von Informationen im Jahresabschluss die Entscheidungen vernünftiger Stakeholder beeinflussen würde. Diese Definition ist absichtlich etwas vage, da unterschiedliche Unternehmen unterschiedliche Wesentlichkeitsschwellen haben. Was für ein Unternehmen mit 250.000 US-Dollar Jahresumsatz wesentlich ist, muss für ein Unternehmen mit 1 Milliarde US-Dollar Jahresumsatz nicht wesentlich sein. Aus Design-Sicht liegt der Hauptwert dieses Konzepts in der Klassifizierung verschiedener Kategorien von Finanzdaten
High Level Data Flow
Business System --(Financial Events)--> Sub Ledger(s) --(Summarized Accounting Entries)--> General Ledger
Die drei wichtigsten Ziele eines Buchhaltungssystems
- Genauigkeit (Accurate): Finanzaufzeichnungen müssen den bekannten Zustand des Geschäfts widerspiegeln
- Beispiel: Wenn 10 Produkte zu je 9,99 US-Dollar verkauft wurden, sollte die Summe in den Finanzaufzeichnungen 99,90 US-Dollar betragen
- Das klingt offensichtlich, aber beim Aggregieren von Tausenden oder Millionen Transaktionen können einfache Summierungs- oder Rundungsfehler zwischen Systemen zu erheblichen Ungenauigkeiten führen
-
Wastemans Notiz
- Viele sagen, Naming sei das schwierigste Problem in der Informatik, aber ich glaube, Addieren ist das zweitschwierigste
- In den vergangenen Jahren habe ich bei der Arbeit an großen Finanzsystemen unzählige Fälle gesehen, in denen sehr kleine Bugs große Unterschiede in den Daten verursacht haben
- Fang gar nicht erst mit Summen über
floatan. Ich habe auf die harte Tour gelernt, warum man immer Ganzzahlen verwenden sollte
- Finanzaufzeichnungen müssen vollständig (complete) sein
- Genauer gesagt müssen Nebenbuch und Hauptbuch zu einem bestimmten Zeitpunkt alle Geschäftsaktivitäten vollständig abbilden
- Wenn es Ereignisse gibt, die stattgefunden haben, aber nicht in den Finanzaufzeichnungen enthalten sind, ist das System nicht vollständig
- Das bedeutet nicht, dass Eventual Consistency nicht erlaubt ist
- Man muss wissen, wann die Daten vollständig sein werden, damit man Stakeholdern mitteilen kann, dass die Daten finalisiert sind
-
Wastemans Notiz
- Auch Vollständigkeit sicherzustellen ist ein erstaunlich schwieriges Problem
- Wenn Systeme wachsen, können Daten beim Durchlaufen mehrerer Systeme versehentlich verändert werden oder verloren gehen
- Prüfbarkeit (Auditable): Finanzaufzeichnungen sollten leicht prüfbar sein, damit Stakeholder Fehler erkennen und die Geschäftsleistung korrekt messen können
- Aktualität (Timely): Das Buchhaltungssystem muss die spezifischen Anforderungen des Geschäfts erfüllen
- Für kleine Unternehmen kann es reichen, alle Zahlen am Monatsende gesammelt auszugeben, große Unternehmen wollen in der Regel jedoch Systeme nahe an Echtzeit
- Dadurch können sie ihre Finanzlage den ganzen Monat über überwachen, schneller Entscheidungen auf Basis von Finanzdaten treffen und den Zeitdruck beim Monats- oder Quartalsabschluss verringern
- Was auch immer der Bedarf ist: Unser Buchhaltungssystem muss die Anforderungen des Geschäfts erfüllen und genau das leisten, was dort unter rechtzeitig verstanden wird
-
Wastemans Notiz
- Bei der Frage der Aktualität verlieren sich Menschen oft in Diskussionen über Batch- versus Streaming-Systeme
- Meiner Ansicht nach ist das für die meisten Systeme keine wichtige Unterscheidung
- Relevant wird es, wenn man sich um sehr kurze Latenzen im Bereich von Sekunden bis Minuten kümmern muss
- Trotzdem hört man erstaunlich oft Leute darüber streiten, was man tun sollte, obwohl die Nutzer nicht mehr als ein paar Aktualisierungen pro Tag brauchen
- Nur weil etwas angefragt wurde, heißt das nicht, dass es auch wirklich nötig ist
Die drei wichtigsten Engineering-Prinzipien, die ein Buchhaltungssystem einhalten sollte
- Unveränderlichkeit (Immutability) und Dauerhaftigkeit (Durability) von Daten
- Ermöglicht Prüfbarkeit und hilft damit beim Debugging und bei der Genauigkeit
- Wenn Daten unveränderlich sind, kann man den Zustand des Systems jederzeit festhalten
- Das macht es sehr einfach, den Zustand der Welt aus einem früheren Zustand neu zu berechnen, weil kein Zustand verloren geht
- Daten, die einmal in Finanzaufzeichnungen festgehalten wurden, dürfen nicht gelöscht werden
- Jede Korrektur am System sollte als neue Finanztransaktion dargestellt werden
- Beispiel: Wenn es einen Bug im System gibt und ein Service versehentlich als für 1.000 US-Dollar verkauft gemeldet wurde, obwohl es 900 US-Dollar hätten sein müssen
- Um diesen Fehler zu korrigieren, muss man zuerst den zur fehlerhaften Buchung gehörenden Buchungssatz stornieren und anschließend den Buchungssatz mit dem korrekten Betrag neu erfassen
- Daten sollten in der kleinsten Einheit erfasst werden (Data recorded at the smallest grain)
- Ähnlich wie das obige Prinzip ist auch dies entscheidend, um einen klaren Audit Trail zu ermöglichen
- Auch wenn Finanzberichte und Hauptbuch aggregiert sind, werden sie aus feiner granulierten Ereignissen berechnet
- Wenn Daten nicht verständlich sind, braucht man die feinste Granularität, um das Problem debuggen zu können
- Wenn Daten auf der niedrigsten Granularitätsstufe gespeichert werden, lassen sich auch aus diesem Datensatz abgeleitete Daten sehr einfach korrigieren
- Wenn ein einzelner unveränderlicher Datensatz die zentrale Source of Truth für alle Sichten auf diese Daten ist,
- muss man zur Korrektur einer Sicht nur die Daten korrigieren und anschließend die Pipeline zur Erzeugung dieser Sicht erneut ausführen
- Ähnlich ist es, wenn Buchhalter bereit sind, die Bücher abzuschließen,
- dann gleichen sie alle aufgetretenen Transaktionen und Kontosalden ab, um zu prüfen, ob die Bücher korrekt sind
- Wenn Abweichungen gefunden werden, können sie sich bis zur genauen problematischen Transaktion vorarbeiten
- Es muss idempotent sein (Idempotency)
- Jedes finanzielle Ereignis darf nur einmal verarbeitet werden, und Duplikate in Finanzaufzeichnungen würden offensichtliche Ungenauigkeiten verursachen
- Aus diesem Grund muss jeder Code, der Finanzaufzeichnungen erzeugt, idempotent sein
- Idempotenz bedeutet, dass sich das Ergebnis nicht ändert, auch wenn eine Operation mehrfach angewendet wird
- Mit anderen Worten: Auch wenn ein Finanzereignis mehrfach verarbeitet wird, muss das Ergebnis identisch mit dem der ersten Verarbeitung sein
Best Practices
- Für Geldbeträge Ganzzahlen bevorzugen: Dadurch werden arithmetische Operationen deutlich einfacher.
floatvermeiden - Eine Granularität für Geldbeträge unterstützen, die Präzisionsverluste bei Währungsumrechnungen minimiert
- Wenn nur mit Dollar gearbeitet wird, kann eine Darstellung in Cent ausreichen
- Für globale Unternehmen sind Mikrounits oder Dezimalzahlen wie
DECIMAL(19, 4)vorzuziehen - In Finanzsystemen sind Dezimalzahlen beliebt, aber in Ad-Finanzsystemen sind Mikrounits der Standard
- Eine konsistente Rundungsmethode verwenden: Je nach Rundungsverfahren kann es zu erwarteten Beträgen und wesentlichen Abweichungen kommen
- Etwa alle Werte ab 5 auf die nächste signifikante Stelle aufrunden und 4 oder weniger abrunden
- Oder immer aufrunden usw.
- Entscheidend ist, systemweit konsistent zu bleiben (wenn pro Transaktion 1 Cent Unterschied entsteht, sind das bei 10 Millionen Transaktionen 100.000 US-Dollar Differenz)
- Währungsumrechnung so lange wie möglich hinauszögern: Eine frühe Umrechnung kann Präzisionsverluste verursachen
- Die Umrechnung verzögern, bis die Aggregation in der lokalen Währung erfolgt ist
- Ganzzahlige Zeitdarstellung verwenden: Etwas umstritten, aber sehr zu empfehlen
- Unterschiedliche Bibliotheken parsen Zeitstempel zu Objekten jeweils unterschiedlich
- Es ist besser, sich diesen Ärger zu ersparen und Ganzzahlen zu verwenden
- Unix-Timestamps oder UTC-basierte ganzzahlige Datetime-Werte funktionieren ebenfalls einwandfrei
- Je weniger Datentransformationen zwischen Systemen nötig sind, desto besser
-
Wastemans Notiz
- Bugs rund um die Sommerzeit habe ich noch gar nicht erwähnt. Mit monoton steigenden Ganzzahlen kann man sie komplett vermeiden
- Wenn du auf
datetimebestehst, verwende zumindest UTC. Erstaunlich viele sehr große Unternehmen verwenden Zeitstempel, die nicht auf UTC basieren
2 Kommentare
Das ist wirklich sehr nützlich. Wenn man Typumwandlungen (
decimal,float,double) oder Rundungen ohne gründliches Nachdenken und ohne Abstimmung im Team einfach so vornimmt, kann das zu großen Problemen führen.Hacker-News-Kommentare
Betonung der Bedeutung einer konsistenten Rundungsmethodik
Empfehlung, Zeit als Ganzzahl darzustellen
Empfehlung, für Buchhaltungssysteme relationale Datenbanken zu verwenden
Die Hauptziele von Buchhaltungssystemen sind Genauigkeit, Auditierbarkeit und Aktualität
Meinung zur Vollständigkeit von Buchhaltungssystemen
Für globale Geschäfte wird die Verwendung von mindestens 8 Nachkommastellen empfohlen
Erwähnung der Bedeutung der Benutzeroberfläche (UI)
Erklärung der Unterschiede zwischen Batch-Verarbeitung und Stream-Verarbeitung
Teilen von Erfahrungen beim Aufbau eines Rechnungssystems mit TypeScript
Empfehlung, Klassen aus der Standardbibliothek zu verwenden
Erklärung der Schwierigkeiten bei Rundung und Datenaustausch
Teilen von Erfahrungen mit API-Arbeit für zehn der größten Banken in den USA
Empfehlung von Martin Fowlers "Accounting Patterns"