Making User Agents Useful

I originally published this post on the Clarify.io blog. It is reproduced here with permission.

If you build or support APIs, you’ve probably asked your team:

How many Java developers use our API?

Has anyone upgraded to our latest Ruby Gem?

Is going to that PHP conference going to be useful?

Fundamentally, they make sense. We want to know who is using our system, what they’re doing with it, and how to find current/new customers to engage with. But too often often, we have a major problem:

You can’t answer any of them.

In some cases, you can track signups from specific events via coupon codes, traffic to the different languages sections in your documentation, check the download pages of your favorite package manager, or even survey your customers but unfortunately, none of these directly relate to usage. Even worse, most of these methods are error-prone, dependent on customer responses, or take manual collection. They’re inaccurate at best and deceptive at worst.

We decided to take a radically different but incredibly simple approach: User Agents.

If you’re not familiar with them, user agents are a simple concept:

In computing, a user agent is software (a software agent) that is acting on behalf of a user. When a software agent operates in a network protocol, it often identifies itself, its application type, operating system, software vendor, or software revision, by submitting a characteristic identification string to its operating peer.

That “characteristic identification string” is the most important part. It what tells a server that you’re using Chrome vs curl vs Safari. While that’s great for a starting point, there are no hard and fast requirements on what to include, so we’ve added a few things.

If you looked at any of Clarify.io’s helper libraries, there’s a common pattern:

PHP:

$this->client->setUserAgent($this::USER_AGENT . '/' . PHP_VERSION);

Ruby:

def user_agent
    ruby_version = "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
    "clarify-ruby/#{Clarify::VERSION}/#{ruby_version}"
end

Python:

user_agent = __api_lib_name__ + '/' + __version__ + '/' + PYTHON_VERSION

Java:

userAgent = "clarify-java/"+SDK_VERSION+"/"+System.getProperty("java.version");

which creates a string like:

clarify-language/x.y/a.b.c

or

clarify-php/1.0/7.1.3

The first portion is simply their language of choice. This gives us hints on how and where to reach potential users both in online communities and which events to attend, sponsor, etc.

The second portion – x.y – is the library version. Using this, we can track upgrade patterns and how long different versions of libraries are live. At a later time, we could theoretically track this back to specific accounts and contact users about new features their old library doesn’t support.

Finally, the third portion – a.b.c – is their language version. As we consider new language constructs or features for the libraries, this tells us how many could actually use it versus how many will have a bad day when we crash their app.

And voila. Now we have great information.

Going back to our original questions, they’re easy. Now we don’t have to bug our users, we just query the system and ask. In fact, we have a regular report that collects user agent trends and sends them to the entire team.

For context, approximately 80% of our requests comes through a helper libraries so while the above is great information, we know it is still not the whole picture.