58 comments

[ 3.4 ms ] story [ 274 ms ] thread
The naming of this is unfortunate [1].

[1]: https://moonpig.com

Quite! I and any others from the UK are likely to assume it's something to do with an annoying virtual/printing greetings card company.
Oh, I actually thought that this was something to do with the greetings card company.

Seconded.

Further to which, I suspect this might run into trademark difficulties.
There's no likelihood of consumer confusion between a billing software package and a greeting card store. Trademarks are not total ownership of words.
Those two sentences are completely unrelated. The first sentence is false if the two companies have the same name. The second is true.
First, the companies don't have the same name. It's the name of a software package, not the company that created it.

Second, that's not relevant, but is relevant to the rest of my comment. Trademark law does not bar two companies from having the same name. There are tens of thousands of companies with the same name. Most words have hundreds of separate listings in the trademark registry. Ever heard of Delta the airline, Delta the faucet company, Acme the laboratory, Acme the furniture company, Acme the grocery store chain, Apple the computer company or Apple the record label?

Trademarks are limited both regionally and by the categories of goods and services the marks are registered under. If there is no likelihood of consumer confusion, there is not going to be infringement. Moonpig's trademark is limited exclusively to "printed matter, namely, books in the field of humor, stationery, greeting cards, and calendars".

I did too, first thing I thought when I saw this.
The first time that I heard an annoying advert for the card company "moonpig.com" I pretty much heard it as "all_the_good_domain_names_are_taken.com" http://www.youtube.com/watch?v=MEBy67ZUrxQ

But apparently "moonpig" is not just a totally random name, if two different groups can come up with it.

moonpig.com is an interesting startup and exit story. Moonpig was his nickname at school. He was a rotund child and not terribly popular. he had the last laugh though exiting for several hundred million.
> No fucking floats

<rant> Every time I see floats being used for stuff like currency, which is still more often than not, I'm reminded how very immature and unprofessional the business of programming still is. This situation has barely improved in the 25+ years I've been in IT.

For fuck sake, we're not talking "advanced" stuff like architecture, algorithms, unit testing, SOLID, or whatever, but basic use of simple tools that's equivalent to understanding that you don't use a hammer to drive in a screw.

I can't imagine any other profession where basic stuff is executed so cluelessly unless it's a deliberate scam.

Right now, as we speak, clients and/or employers are paying many thousands of dollars/euros/whatever to have brand new software build by programmers who will use floats for what are supposed to be exact amounts of money the core business of their employer/client depends on.

It's a pathetic state of affairs which makes the phrase "software is eating the world" an utterly terrifying idea.

And now there's a movement that wants to teach everyone "how to code".

The apocalypse will be caused by a rounding error. </rant>

  > This situation has barely improved in the 25+ years I've been in IT.
Improved? 25+ years ago this would have been written in COBOL using decimal arithmetic as a matter of course — because 55 years ago the designers of COBOL knew better than to put floating point in a business language.

(This is not a defense of COBOL, which is terrible in so many ways, because they didn't know much about programming in 1955 — but they knew they had to understand the problem domain.)

If we had evolved with only eight fingers we would be much better off. Also sometimes you do use a hammer to set a screw so it won't move when you go to drive it.
So how does the latest darling of the webapp world, nodejs, handle this? In javascript, the only numeric type is float.
The same way you do it in Perl: you store rounded millicents. If there's never any fraction in your float, your math will never have floating point issues, and your Javascript VM will use integers internally.
> I can't imagine any other profession where basic stuff is executed so cluelessly unless it's a deliberate scam.

Off the top of my head: Electricians. I've seen first-day-on-the-job-level mistakes in every single property I've rented. Most licensed electricians (nb: I am in the UK) don't seem to be able to safely wire a simple light switch.

Although I agree with most of the article, I think the moment the words "millicents" and "billing system" are used in one sentence you should stop, drop, and ask yourself what the heck you're doing.

Just save billing stuff in cents and do internal billing math using arbitrary precision integer math and round to %0.2f at the end. Nobody cares about mistakes that involve fractions of a cent, or whether their subscription lasts 1 year or 365 days or 52 weeks of 7 days. Just pro-rate in the easiest way possible and round in favor of the customer. You just don't need any of this fraction-of-a-cent BS.

If your billing math is easy you can even explain how it works in the FAQ so customers won't feel cheated. If your billing is complex and uses millicents in an misguided attempt to "do the right thing" then you can't explain to your customers how it works and why it's fair. That's no good from the customer's point of view.

Billing systems are easy to over-engineer and in my experience you just want a system that's straightforward and correct. That way you can focus your energy on more important stuff.

As I read it, millicents made the billing math simple and easy to explain. I bet most people can understand millicents more easily than arbitrary precision integers...
Arbitrary precision arithmetic is known to most people as "math".
He didn't say anything to that. He was talking about explaining the principles.
He gave good reasons why they used millicents, one of the best reasons was that it prevents things like a customer owing $0.006 and therefore getting a "past due" bill for $0.00. Another was that it prevents real errors from hiding as possible round-off errors. I found the reasoning convincing myself, and yes, I have worked in the financial industry.
I agree that using regular floats for money is insanity. Aside from that I'm not convinced at all, because the situation never arises where you want to charge people 0.01 cents or 17c for that matter. Who in their right mind is going to generate an invoice for 17 cents?

If people have a small amount of credit just give them an extra day of service. If they have a small negative credit either ignore it or subtract one day the next time they renew.

If there is a case for using milicents I sure haven't seen it.

Ad impressions are priced at e.g. 0.40 USD per thousand impressions - that is, 40 millicent. Personally, I prefer to think of that as 400 microdollars, but to each their own.
40 millicents? Or 40 cents? There's a difference.
40 cents per thousand imprressions equals 40 Millicents per impression.
As I understood it, the basis for their system was that each customer's account contains the actual current balance (remaining value) at any point (any given day). If you charge $10/year for a service that's 2.739726027 cents per day. You can't have a system like that and round to pennies without some serious error at the end of a year.

I'm not sure I understand the value of a system like that though. In the systems I've used and built, most customers see their service as an annual thing that renews on a particular date every year, for which they pay an annual fee. Doing it as a daily fee simplifies the special cases like converting between plans but makes it harder to deal with "your annual plan always renews on this date".

The way I understood it is they chose the resolution of millicents because it was precise enough to allow an annual plan that was never converted to have this behaviour, yet be cleanly convertible.
Because if you do all of your internal calculations in cents, then the rounding errors add up to a significant amount in not very much time.

Let's say you have a service that is $20 per year. That is 5.475¢ per day (I'm assuming a year of 365.242 days; if you do an integral number of days and account for leap years, then it will be a different daily rate in regular years and leap years). If you are doing calculations which pro-rate certain things per day, such as allowing people to transition between different subscription types while applying their existing balance to the new subscription, and you rounded that down to 5¢ per day, then when you multiplied that back out to a year it would only be $18.26 per year, not $20 per year. That's a pretty substantial difference.

Where you fix your precision depends on how much error you're willing to deal with. In the article, he describes how using millicents leads to an error of 165m¢ (.165¢) per year, if you apply the rounded daily rate and multiply by the number of days, which is deemed acceptable for their purposes. For a much more extreme example of precision, Tarsnap quotes their prices in picodollars per byte per month (https://www.tarsnap.com/picoUSD-why.html) and performs all calculations in attodollars per byte per day.

What do we do if they honestly thought this was the easiest way to pro-rate?

(I get the idea that they generally wanted to be able to report the remaining subscription in terms of days)

I don't understand, what's wrong with decimal(28, 8)? That gives you plenty of precision, as well as large scale in case you use a hyperinflationary currency.
Due to the formatting and the use of Perl I had to check the date of the article as I thought it perhaps was a 10 year old article. But it was from last week.

But that is current me being a slight code snob as I did use Perl as my prime language for a few years but that was 13 years ago.

As someone who have worked with many payment and billing systems I can see some of the pain addressed. But also reinventing the flat wheel is something I try to avoid and keeping things simple so for example these days I use BigDecimal, BigInteger datatypes and similar in more modern languages.

Ps. as someone who just ordered cards from moonpig.com, the name was initially confusing.

Yeah it seems kind of an odd choice to write the thing in Perl. I guess they have some other Perl stuff they want to link it with perhaps?
Perl has the most libraries and most active extension system of any programming language available. It runs the entire Internet.
Mark Jason Dominus is a Perl guru -- it would probably be a surprise to see him write it in anything other than Perl; among other things, he wrote the book, "Higher Order Perl", which is basically SICP for Perl or, as he describes it: "Higher-Order Perl is about functional programming techniques in Perl. It's about how to write functions that can modify and manufacture other functions."

(The book is available for free online, btw: http://hop.perl.plover.com/

There's a link there to a page translating the concepts into Javascript, btw -- there's a similar link for Ruby but it doesn't work, nor does a revised link -- changes the word "category" to "categories" in the URL -- that I found Googling, but this does: https://web.archive.org/web/20130315020240/http://blog.grayp... )

It's interesting to see the differences in design decisions they used vs. the "ledger" program that I've used for a while (http://ledger-cli.org).

Specifically, that ledger internally uses an arbitrary precision library to store data, then only cuts down or rounds numbers at the display level, and the use of libraries and a database vs. a file format and highly optimized code.

A relational database would have been a much better fit for this system.

The author initially states that all relational databases are good for is speed but then goes on to highlight flaws in their object store that could all have been mitigated by using an RDS:

* schema changes without needing to write programs to update all your data

* data aggregation (and in fact any sort of adhoc reporting)

* data storage independence.

There's also no mention of data integrity but this is a big risk as soon as the original authors are no longer involved.

Object oriented data stores are hierarchical/network databases. Relational databases were introduced to replace these years ago and for good reason.

But... if your data is hierarchical in nature, then trying stuff it into a relational database results in a giant clusterfuck.

Just use the right tool for the job.

How so? Modern RDBMS' are more than capable of manipulating hierarchical data.
I'm not sure I can take seriously anyone who thinks an ACID-compliant RDBMS sucks for ledger accounting. Is it the RDBMS' fault that you don't know how to do extremely basic transaction recording? This isn't reinventing the wheel here, you don't have to design your own schema, there are plenty of examples readily available on the 'net.
"Happily, Moonpig did not have to deal with multiple currencies. That would have added tremendous complexity to the financial calculations, and I am not confident that Rik and I could have gotten it right in the time available."

Yeah, so, that tells me that sadly, regardless of the rest, it sucks (sorry). Not supporting multiple currencies, as he says, does vastly simplify things, but is also a massive feature-gap, as any merchant of scale will deal in multiple currencies. Now try doing it with multiple currencies, and still avoiding floating point arithmetic. Oh, and also ensure that it'll deal with charging sales tax/VAT on transactions of £0.01 correctly.

In today's world, yes. From the article, it looks like the Moonpig authors were dealing with dot-com era billing (everything in US dollars in the late 90s).
Looking at github it appears they started building it in 2010.
Sounds like you'd recommend going with a vendor like Sage? Or that IC Group should use an accounting system which doesn't match their business requirements?
Not necessarily, just that forward planning can be a useful thing. "We'll only ever trade in USD" is a phrase I've heard many times, and rarely does it hold true. Time has a habit of changing plans, and baking in support for possible future directions from the outset is a good way of hedging risk.
That's risk avoidance, when the smart money's on risk management. You might need to travel offroad, or transport a ton of cargo, or get from A to B in 60 seconds, but you don't own a motorcross bike, ¾ ton truck, or sports car right now: you wait until circumstances change and rent, borrow, or buy the vehicle which meets your needs and budget.

Although now you are arguing that they should have designed the system with the possibility of multiple currencies, rather that actually supporting them. There's not enough information in the article to determine if support for multiple currencies is possible or not, so I guess you read the source?

The crazy thing about software is that when business requirements change, you can change it. If they don't need to support multiple currencies today, it's silly for them to put in the effort now.
Very true, however design decisions made at an early stage can make changes of that ilk challenging, and even nigh on impossible without significant rewrite, down the road.
How would support for multiple currencies make it harder to avoid floating point arithmetic? That makes no sense..
Exchange rates can have many sig figs - which become very significant when dealing with conversions of large amounts. Lose precision, and conversion, in plenty of scenarios, will result in rounding errors if you convert back.
Times and time zones suck ... Floating-point arithmetic sucks ... Rounding errors suck ... Relational databases suck

Guess what, any decent relational database will have a MONEY or at least a fixed precision DECIMAL type, as well as DATETIME and all the ancillary functions for computing intervals and adding and subtracting time. Half of your problems were because you weren't using a data store that properly represented your data.

More a discussion about why not to use PERL than anything else.

Also, hire a branding expert - "moonpig" is awful in every way possible.