During my time at Divvy, I’ve been exposed to the growing and evolving FinTech sphere. While the field covers a wide range of applications and industries, the one thing that is common across companies is the importance of financial information. A central foundation to this is some sort of ledger that allows for the consistent tracking of financial movement between entities.
When Divvy set out to automate our accounting system, we were surprised to find that there was almost nothing available as far as open source libraries or even software-as-a-service to handle this for us. So we had to build our own. Flash forward to today, and it’s become clear this pain point was consistent across the industry, forcing everyone to build their own system.
Building software to automate accounting seems like a fairly straightforward problem, but there are a number of complexities that aren’t obvious at the outset. Luckily, accounting has a long and established foundation that acts as a starting point for a ledger. This means that while we were building our system from the ground up, we had been gifted with the basic architecture of agreed upon accounting standards. Double-entry bookkeeping is the central concept and some elements from “generally accepted accounting principles” (GAAP) are worth brushing up on (I say “some” only because many tenets of GAAP apply more specifically to humans doing the accounting).
There could be an entire book written about building a software ledger, and I’m sure one day there will be. For now, I will share a few quick pointers and pitfalls I ran into while building out a ledger system at Divvy.
What we did correct from the start
Building out our ledger system was a journey with a lot of learning along the way. However, there were a few key pieces we got right from the beginning and it ended up saving us a lot of trouble down the road.
Ledger entries are an immutable event stream. If you read information on double-entry bookkeeping and try to directly implement an analogous system in software, you end up with an event stream model. What this means is that each individual transfer of money is an event, and questions about the state of the ledger are answered by combining these events in various ways. Fortunately, within software engineering, event-sourcing is exactly the same concept with the same set of concerns, so there is a rich world of information about how to build these systems reliably and so that they can scale appropriately.
Every entry can be traced back to its origin. What this means specifically will vary by context, but in general, this means that by using a reference (or a series of references) you can trace any individual transaction to an external source. For Divvy, that meant that any entry could be tied back to the original bank statement and bank transaction where the transaction occurred. For others, this may mean tracing back to a payment processing system, or even an audit log of data entry for a paper check that arrived in the mail.
This is important for two reasons: auditability and regenerability. Auditability because we are dealing with financial data here, so it is critical that you can explain any decision or reporting that your system produced. Regenerability because humans write software, and humans make mistakes. If it is discovered that there is a bug in the way entries were being generated, it is important to be able to look back at the original sources to determine what the entries should have been so that a correction can be issued with confidence.
What took us a little longer to get right
With the above fundamentals in place, we were able to avoid anything too painful, but there were some challenging pieces that I can hopefully you can avoid when building your own ledger system.
Build an amendments system for correcting the record. When considering the stream of entries, there are two relevant timestamps to think about – when the transaction occurred and when the entry was added to the ledger. Ideally, these are the same or nearly the same. However, in reality mistakes are made, bugs are deployed, or paper checks don’t get entered in immediately. Since we are dealing with a stream of immutable entries, we cannot go back and edit them. Instead, we amend them by adding new entries into the event stream which have a date of occurence and the point in time that they are correcting, but still track that they were added to the ledger later on. In this way, when determining an account balance, or some other answer, you can generate both what the transaction history should have been as well as what the ledger would have said it was at the time. This is especially useful when reconciling current account balances with account balances that appear in reports generated in the past. It should be possible to regenerate an old report exactly as it would have appeared at the time, even if corrections have been issued since.
Floating point arithmetic is a headache. This seems like a minor consideration at the beginning, but this issue can grow into a painful problem over time. It feels natural to model currency amounts in dollars (or your equivalent), with cents being represented to the right of the decimal. This is, of course, how we write it out every day ($5.99 rather than 599 cents). Because of the way floating point numbers are represented digitally, however, repeated calculations using them can cause a fractional “drift” away from the correct amount. When summing a stream of entries, this drift can become nontrivial. The solution here is to either use a dedicated decimal type in your language of choice or to represent the amounts in whole numbers of pennies. This will save you the headache of constantly rounding and checking if sums “approximately match” one another.
This post only scratches the surface, but hopefully these tips will help you to overcome a few of the challenging aspects of building a ledger without having to learn the hard way. Given the growing FinTech space and the need for ledger technology, I imagine we won’t have to build our own ledgers forever.
At Divvy, we intend to eventually open source core elements of our system, and at FinTech meetups I have heard talk of ledger-as-a-service being on the radar of multiple startups. The future will be easier, but for now let’s try to learn from one another’s experiences and avoid having to learn the same lessons separately.
Ryan Coonan is a Senior Software Engineer at Divvy