MRR (monthly recurring revenue) is arguably the most important metric for SaaS companies to be tracking. At many growing SaaS companies, including GoSquared, it’s an invaluable KPI (Key Performance Indicator) and allows us to track growth against churn on a monetary level.
Track Everything
Every single change to any member of your userbase should be tracked. Did they change their name? Did they add a site? Did they start tracking?
Knowing what’s happened to a user over their lifetime is essential – and tracking changes to their subscription is necessary for monitoring MRR.
MRR Definition
How you define MRR is important to what value it can provide you as a metric. Many SaaS companies have both monthly and yearly plans, so misleading skews need to be avoided.
You could, for example, just add up the amount of money coming into the bank account each month – but that’s not MRR, that’s just the inbound revenue for that month.
Segmented MRR
MRR is best when segmented into a few different types:
- New MRR from new customers
- New MRR from account upgrades
- Lost MRR from account downgrades
- Lost MRR from expired subscriptions (lost customers)
The sum of those 4 metrics gives the net MRR, and each of the metrics individually can be very useful when tracked over time.
Yearly Plans
Yearly plans (or non-monthly billing cycles) have their values divided by their monthly length. So if a yearly plan cost $240, the value tracked in MRR should be $240 / 12 = $20.
“But that looks like less money than we actually got”
All we’re doing is spreading the value of the subscription into equal monthly amounts, as if it’s being paid monthly.
MRR is meant to be an indicator of growth and the amount of revenue you can expect to make on a recurring basis. It’s not meant to be an exact figure, and will almost never match up with actual money in.
If you’re looking for a metric to track actual revenue into your bank, use that instead of MRR – but be aware that it can be misleading.
Storage
MySQL is great for tracking internal metrics – it’s easy to query (are your marketing team really going to connect up to MongoDB and know how to use it?) and very reliable.
We have a simple user_events
table in MySQL which allows us to do cohort analysis and much more. We have many millions of rows in this table, and we still aren’t quite tracking everything just yet. This table contains user_id
, event_type
and timestamp
columns, as well as an extra
column that contains JSON encoded information that could be relevant for the future.
We receive push notifications from Recurly whenever something happens to a subscription, and they’re all stored in the user_events
table.
To allow efficient MRR tracking, we exported the subscription change user events and put them into a new plan_changes
table, containing user_id
, timestamp
, type
, old_value
, new_value
, currency
and yearly
columns. The type
column can be any of created
, updated
or expired
. Note: the value columns will need the be SIGNED to allow some of the querying below.
Sample rows
user_id | timestamp | type | old_value | new_value | currency | yearly |
---|---|---|---|---|---|---|
19263 | 2014-03-24 12:56 | created | 0 | 50 | GBP | 0 |
64 | 2013-03-24 13:55 | updated | 120 | 20 | USD | 0 |
5784 | 2014-03-24 15:02 | expired | 9 | 0 | USD | 1 |
46784 | 2014-03-24 16:11 | updated | 99 | 240 | USD | 0 |
Querying
Querying the data and getting some useful insights is, of course, the most important part. The table structure above allows for some easy querying…
Note: we’re ignoring the currency here, but segmenting by currency and converting those figures into a single currency will make the figures a lot easier to digest (and will also prevent them from being misleading)
Total MRR
SELECT SUM(new_value - old_value) AS MRR FROM plan_changes
Net MRR by Month
SELECT monthname(timestamp), year(timestamp), SUM(new_value - old_value) AS MRR FROM plan_changes GROUP BY month(timestamp), year(timestamp) ORDER BY timestamp DESC
Total Lost MRR by Month
SELECT monthname(timestamp), year(timestamp), SUM(new_value - old_value) AS MRR FROM plan_changes WHERE new_value < old_value GROUP BY month(timestamp), year(timestamp) ORDER BY timestamp DESC
New MRR from Upgrades by Month
SELECT monthname(timestamp), year(timestamp), SUM(new_value - old_value) AS MRR FROM plan_changes WHERE type = 'updated' AND new_value > old_value GROUP BY month(timestamp), year(timestamp) ORDER BY timestamp DESC
Conclusions
Graphing the data can reveal some really interesting trends. Christoph Janz has put together a great dashboard/spreadsheet which will automatically graph many useful metrics once a few data points are inserted.
Remember: track everything.