What are the specific challenges to writing financial software? What are common mistakes you see? What are common data structures for representing money (both the common incorrect implementations but also the correct implementations)?
Also, provided you have a data structure that can represent money, you should presumably be able to serialize that data structure and store or send it just like any other data, right? Why do you need special database or wire format support? The trivial example is to marshal it to JSON put it on disk or write it to the network using the protocol suite of your choice, right?
String (json), decimal/numeric (db) is enough to passively store amounts. Calculations and rounding going to be funny though. E.g. split $10 bill in 3 exactly the same parts, store, sum up to $10 again
Are there actually some systematic approaches to handle these cases? Or are there some libraries making this easier? We have so many places where we keep track of the offset and spread it over the items afterwards to mitigate this. It’s annoying as it’s most often a two step process and e.g. becomes even more complex when you have constrains on the numbers like applying discounts.
In this particular case, it's easier to think of the problem as "allocating $10 among 3 parties" rather than "dividing $10 among 3 parties." The latter insinuates equal distribution. Often, having the 3 parts sum back up to $10 is more important than giving an extra penny to one person and not dividing equally.
What I've used before is "Express the value in pennies. Divide by X (number of parties). Take the whole number part and give it to each person evenly. Take the modulus and distribute it one by one to each remaining party, until there is nothing left."
Example: allocate $10.01 among 3 parties:
1. You have 1001 pennies
2. Divide by 3 - give each person 333 pennies
3. Take the modulo 1001 % 3 - you have 2 pennies remaining. Time to distribute them round-robin!
4. Give one penny to person A. You have 1 penny remaining.
5. Give one penny to person B. You have no pennies remaining.
If you do this many times, randomly establish the order of the parties each time you round-robin them. In that case, no one will be systematically under-allocated, just because their name starts with the letter Z.
This is not a universal truth, and each situation is different. It may not apply to other kinds of money dividing situations.
I never said you need special database or wire format. Maybe if you are using JSON, transfer monetary values as strings rather than decimal or float data types. Some databases can actually handle money, some don't. Those that do not, usually require you to store money as strings to not lose information. When you get warned by your DBA that the database can't do arithmetic on strings, tell them that "thank you, it could not do correct arithmetic anyway".
Just use integers, and fields ending with "_cents".
There is no ambiguity, rounding problems are not even an issue, serialization and deserialization will pose no problem when dealing with external actors, and you can still sum money easily in your db requests.
Problems will arise when you'll do multiplicative operations on money, for instance when working out taxes. There are precise rounding rules to apply, and the solution is to tackle these issue one abstraction layer above, on the operations rather than the values, because some time you'll want to carry out rounding between each tax operation, sometimes at the end, on one line or on a whole batch of transactions.
Other problems you'll bump into is the asynchronous nature of money flows. You won't realize it with credit cards (well you'll figure it out soon enough when you'll stumble upon "race conditions"), but this becomes explicit when dealing with mandates, direct bank transfers or checks. You need to move the money out of sight of the user in a ledger specific to that person that holds transactions being processed (can takes days or weeks in some case) and move it back to the original ledger if the transaction fail. Otherwise you'll bump into issues of double spending. This is something that is out of your control sometimes (had the unfortunate experience to issue withdrawals multiple times on a big bank's payment processor API and theses dunces sent the money multiples times). Use idempotency keys profusely as well as (distributed) locks. The hardest part is not getting your part right, it's handling external actors bad implementations. Also fuck HTTP w/ hooks. Some actors do not even make sure you received the webhook. The non binary aspect of HTTP is a bullshit argument and I'd gladly trade it for a binary protocol that has strong quality of service mike MQTT, since anyway I'll have to implement some kind of smart broker when issuing orders to a shitty HTTP api anyway.
You said something to the effect of “it’s not enough to have financial libraries, you also need storage (e.g., db) and transfer (e.g., wire formats)” which suggests that you can’t just use a standard library to dump JSON to a file or through an HTTP connection.
But yes, I can see how databases not having a money type with corresponding routines is going to make life harder.
If your database has a MONEY or CURRENCY data type, use that.
Just as you should use proper DATE or DATETIME data types for time and not roll your own with strings, integers, seconds-from-epoch, or any other schemes at least if you want to keep your sanity.
> If your database has a MONEY or CURRENCY data type, use that.
But hopefully only after understanding how it treats these, and whether that's compatible with your requirements.
> Just as you should use proper DATE or DATETIME data types for time and not roll your own
I'm always happy to use the database's date/time format – if it's actually implemented in a sane way and is compatible with my data.
For example, I work with data provided by external partners that specifies dates:
Sometimes they're only specifying the year as a single-digit integer (and you have to guess which one they mean, based on the current date and the hope that the files they send you are not older than 5-10 years). Sometimes there is no year at all. Sometimes the timestamps have an implied timezone, sometimes they're UTC, and sometimes they're supposed to be UTC, but really are in some unspecified local timezone.
In these cases, it can indeed be better to store these as strings and deferring interpretation until you actually process them.
Also, provided you have a data structure that can represent money, you should presumably be able to serialize that data structure and store or send it just like any other data, right? Why do you need special database or wire format support? The trivial example is to marshal it to JSON put it on disk or write it to the network using the protocol suite of your choice, right?