How we optimised our API to reduce network overhead by up to 70%

Possibly the most fundamental improvement we have made to our networking stack so far is the use of Protocol Buffers.

If you're reading this as one of our customers, this means a quicker app and far less data being transferred to and from your device.

Protocol Buffers

The core technology, developed by Google, enables cross-platform communication between all our micro-services and apps in ultra-lightweight binary format.

After implementing, our API throughput has been reduced by about 70% compared to our previous JSON-based interface. A considerable improvement given the large amount of data we have to transfer when dealing with images.

So what does it look like?

As a simple example, here's our protobuf definition of a country object (called a "message").
The numbers are called tags, they uniquely identify each field in the message.

message Country {  
    // The ISO Alpha 2 two letter country code (e.g. IE, UK or DE)
    string country_code = 1;

    // The english name used for debugging purposes
    string debug_name = 2;

    // The localization key for the country
    string localized_name_key = 3;

    // If true float this country to the top of the picker list
    bool major_market = 4;

    // True if we can ship stuff to this country
    bool delivery_available = 5;

    // The pricing zone to use when determining the price of a product
    string pricing_zone_identifier = 6;

Our mobile apps need to know about countries so our customers can select where to have their purchases delivered. When an app launches, it queries our API for a current list of countries we deliver to, the API responds with a list of these country messages.

What's really nice is that when our app needs to send data to the API, it's as simple as initialising a message, setting the values, then calling a 'serialise' or 'data' method on the object to get the serialised binary representation of the message. That binary data can then be sent to the API as a simple POST request.


C/C++ style comments can be used in the .proto definition file; these comments propagate through to the compiled output. So by adopting protobuf, we have all our object models defined and documented in one central repository.
If a change is made to any model, we simply recompile the interfaces for iOS, Android and every micro-service. Win.

It's good to know...

Default Values

null doesn't exist as a concept in protobuf; if you don't set a value for a specific field the message will be encoded with a default value. For numeric types, the default value is 0, for boolean types, the default value is false; for strings, "". Google have documented this really well so it's great to take a read before implementing so you don't get tripped up.

Changing Tags

If you modify the tag-value of a property, each compiled version of that protobuf will need to be recompiled so it can serialise/deserialise the message. It's worth automating this process as it can be tedious to do by hand, especially if you have to compile the protobuf into several languages.

Serialise to JSON

It's good to know that Protocol Buffers can also "talk" in JSON. This can be really useful for debugging if you're watching network traffic through a proxy, or if you write the object to disk and want to inspect it.