Better Analytics

Good analytics on mobile is hard. It can be challenging to implement well, frustrating to test, and even small mistakes can corrupt your data.

Here are some tips if you’re just getting started.


Make your analytics accessible to everyone

Everyone in the company, no matter what their technical skill level is, should be able to answer their own questions about the business at a basic level. This means having a UI (usually third party in the early days), but it also means being conscientious about how the data is formatted. Using plain, clear language throughout is a good first step to making sure technical knowledge isn’t necessary to answer a question.

Track user intent, not just business metrics

There’s a temptation when engineers are implementing analytics to follow the technical architecture, not the user journey. This can lead to analytics that are only understandable to the people who wrote the code.

Tracking user intent, rather than simply tracking a handful of business metrics or technical operations, increases the chance you’ll be able to answer questions about your users in the future as well as the ones you have today, and makes the data more accessible to non-technical folks.

Stay flexible with tool choice

Different third party tools are good at different things, and we never managed to find one that did everything we needed.

Abstracting your analytics calls to a manager makes it easier to add and remove services without touching too much of your codebase, and if you want to take it one step further, many platforms have an HTTP spec and can receive events from your server. Passing events to your own server means you can switch new services on instantly for all clients, without waiting for app updates, and lets you store the data in your own database too — critical if you want to be able to clean up any errors in the data later on.

Test methodically

Analytics needs thorough testing because inaccurate tracking can invalidate your entire data set, or even lead the product and business down the wrong path.

Use a tool that has a live view of analytics events rather than a delay on tracking (Mixpanel is what we used), where you can see events coming in as they’re performed. This makes it easier to manually go through the different user paths and verify that the logging is correct as you go.

Run statistical analysis

Funnels, segmentation and retention analytics are useful but they tend to require that you define the question first. Statistical analysis on your data using services like Statwing can expose correlations in your data that you didn’t know were there, and lead to questions you didn’t think to ask.

Beyond analytics

A good analytics implementation is a fantastic base for a slew of other tools. A/B testing (and all its variations) becomes much more powerful when you can measure its impact precisely, and it also becomes possible to build out context sensitive app behaviors and notifications without much difficulty.

But ultimately the biggest reward is the freedom to ask questions, and get the answers quickly. A good analytics implementation will help you and everyone else in the company understand your customers and your business. So take the time and get it done right!

 

Title image is a co-authorship network map of physicians publishing on hepatitis C, by Andy Lamb. I left it enormous, because I read this.

Declarative vs Imperative programming

I’ve been using declarative programming for the last eight months, and it’s changed the way I approach my code. Managing complexity (the main job of any software engineer) becomes a lot easier when the code is structured more like human logic than machine logic.

I’ve been using ReactiveCocoa, a port of .NET reactive extensions by the guys at Github.

In imperative programming (what most of us do each day), we might have something like this:

function updateTotal {
    float totalPrice = (self.quantity * self.pricePerItem)  —  self.couponValue;
    self.textfield.text = [NSString stringWithFormat:@”$%.0f”, totalPrice];
}

function setQuantity(int newQuantity) {
    _quantity = newQuantity;
    updateTotal();
}

function setPricePerItem(float newPrice) {
    _pricePerItem = newPrice;
    updateTotal();
}

function setCouponValue(float newCouponValue) {
    _couponValue = newCouponValue;
    updateTotal();
}

This code is imperative in the sense that it’s explicitly executing procedures based on previous actions — i.e. it’s telling the app to update the total. But it’s bulky and there’s a lot of redundancy in there.

The problem would be worse if the couponValue, pricePerItem and quantity were variables in other classes. You would have to figure out if the textfield’s view controller is instantiated, and expose the updateTotal method publicly, and you’re forced to write a setter method for each influencing variable, or be stuck calling updateTotal from a bunch of random places in your codebase and coupling things needlessly.

Here’s where Reactive Cocoa is great:

RAC(self.textfield, text) = [RACSignal combineLatest:@[RACObserve(self, quantity), RACObserve(self, pricePerItem), RACObserve(self, couponValue)] reduce:^NSString *(NSNumber *quantity, NSNumber *pricePerItem, NSNumber *couponValue) {
    float totalPrice = (quantity.intValue * pricePerItem.floatValue)  —  couponValue.floatValue;
    return [NSString stringWithFormat:@”$%.0f”, totalPrice];
}

This essentially says “the text in the textfield depends on the variables quantity, pricePerItem and couponValue, and its value is equal to (quantity * pricePerItem) — couponValue”. Much closer to how we think about these things ourselves, right? Now, if this ever happens…

self.quantity++;

…the total will be automatically updated. The code above only needs to be called once, and if the view controller holding the textfield is deallocated, the bindings will be automatically disposed of. Awesome.

If you want to find out more about ReactiveCocoa, this is what helped me get started:

Functional Reactive Programming by Ash Furrow
The ReactiveCocoa readme
Colin Eberhardt’s tutorial