6 ms·
Some of this complexity is self-imposed. It's flailing around and trying many different things on a slow meandering path to success. Often the trick is to simpl
by jiggawatts 4y ago
Some of this complexity is self-imposed. It's flailing around and trying many different things on a slow meandering path to success. Often the trick is to simply copy what a bigger org has done after a decade of experience.
As a random example, Azure generally bills for resources on an hourly basis.
Suddenly, months, years, calendars, etc... cease to matter. Hours are hours. You can use UTC for everything, ignore time zones and daylight savings, and aggregate however you like, or how the customer likes.
Similarly, consumption-based plans are relatively straightforward. Simply multiply unit costs with the per-unit costs. You can even blend this into the hourly model above, where each hour has a "units consumed this hour" and a "unit price per hour". This way you can handle things like changes to the unit price fairly accurately.
With cut-overs between plans, the trick is to just bill for them as separate line items instead of one thing going through transformations. E.g.: 220 hours of Standard SKU and 500 hours of Premium SKU this month. It doesn't matter how many times the customer changed their mind this month that way.
A nice trick is that data like the above superficially sounds like it'll be huge in a database, but column-based compression works wonders on it. Any analytics database engine, columnar format, or reporting engine should reduce it by at least 30:1 or more. Also, you now have a ready-made reporting system that allows slicing and dicing! In some database engines like SQL Server, this is as simple as making the main billing table use a Clustered ColumnStore index. In more cloud-native land you could use Parquet files in S3 buckets, or whatever.
I'm not saying that complexity doesn't remain, it's just that complexity shouldn't be voluntary. Copy a working model that matches your industry, and then only the essential complexity will remain.
An example of a hard-to-solve essential complexity is currency conversions and taxation when dealing with international customers. That's an externally imposed set of problems that can't be hand-waved away.
- herodoturtle 4y agoGood grief this was informative! Thank you for taking the time to share that.
- AnhTho_FR 4y agoOf course! Thank you for the kind words!
- doctor_eval 4y ago(I'm a billing engineering team leader with more than 25 years experience.) This is a wonderful example of how engineers underestimate the complexity of billing. > Suddenly, months, years, calendars, etc... cease to matter No they don't. You don't send a bill every hour, or even every day. Typically you invoice monthly. And if you invoice monthly, what date do you aggregate the invoice? Setting aside the obvious problem that months are not homogenous, do you start on the anniversary date of signup, or the start of the month? Well, anniversary date is generally better, because it spreads out payments, collections and cash flow over the whole month, which reduces a lot of effort in collections and finance, and reduces the impact of any issues - but it's also significantly more difficult to engineer; how do you bill February if they started billing on the 31st of January? Monthly billing is far easier from an engineering perspective, but is not generally the ideal method from a business operations perspective. And get this, if you start with monthly billing and have significant income, and then you want to change to anniversary billing, you get all kinds of income recognition (P&L) problems that you have to deal with; it's really complex. And what do we mean by "signup", anyway? Do we bill services individually based on when they are created, or do we bill based on the account creation date? What if there is an account hierarchy? Hourly or unit based billing does literally nothing to solve these problems, because they are endemic to the billing domain. > Similarly, consumption-based plans are relatively straightforward. Simply multiply unit costs with the per-unit costs. Right - and what happens when you raise or lower your prices? That has to happen at a particular point in time, which is typically in the future. What if we give tiered discounts (first 1000 minutes at 2c, next 1000 minutes at 1c)? In both cases, we no longer can keep a single per-unit cost, but rather we end up with a cost matrix, which can become really complex when you add other factors (such as discounting). Also, what happens when you are charging, say, 5c per megabyte... and your incoming CDR feed gives you bytes? Do you configure this as 0.00000005 cents per byte? When do you do the rounding? On an hourly basis? Daily? Monthly? What do you display as the unit price to your users? What if another plan is charging 5c per Gigabyte? > An example of a hard-to-solve essential complexity is currency conversions and taxation when dealing with international customers. That's an externally imposed set of problems that can't be hand-waved away. Currency stuff should be very easy in practice, but I agree that tax is hard. That's why you need a multi-stage pipeline for invoice generation. But that's perhaps a story for another post...
- withinboredom 4y agoAfter seeing billing done right, I cringe every time I see billing done wrong. For example, people building foreign keys to prices and receipt items. In what cases do you expect to retroactively change what you charged all customers? Copy that data to the receipt item, don’t give your accountant a headache and heart attack when you change pricing. I’ve also seen people using those same kinds of rows to determine what features a user gets access to. Don’t do that, use capabilities/roles. When a payment succeeds, update the user’s capabilities. When a payment fails, remove capabilities. This also allows support to just give a customer capabilities without a line item or changing their plan, because some customers are just annoying and it is less expensive to give them a feature. It also allows more easily selling custom-tailored solutions to bigger clients since the features aren’t linked to line items. This article is more about usage-based billing, which is probably harder to get right, but feature-based billing is something I’m more familiar with and it’s so annoying to see all the ways people will fuck it up.
- deleted 4y ago[deleted]
- gryf 4y agoYour first point about foreign keys was the first contract I ever did. Someone built a billing system in perl+mysql for an ecommerce site. When prices changed so did historical invoices. I was hired to work this out because the existing developers couldn't. They only realised realised that something was wrong after a customer sued them after being paid the wrong amount in a refund. I rewrote the entire system to snapshot the committed values at the time of purchase and fixing all the old invoices from database backups.
- robertlagrant 4y agoThe way you'd assume they'd do it if they wanted relational values would be to never be able to edit prices, only create new ones.
- withinboredom 4y ago
- altacc 4y agoI think you've possibly simplified by solving a different problem to what's described in the article. The problem space described is subscription plan & pay per use (with what sounds like might be percentage pricing as well). This is quite different from per unit billing. > Some of this complexity is self-imposed Having spent years working on invoicing & payments systems, the business will impose complexity (which you can only say no to a certain amount of) and then legal will pay you a visit and explain all the many rules that apply to billing & invoicing that vary across each country. Internationalization isn't just a tax complexity.