Don’t Make Me Think, When I Consume Your API

I recently worked on the voucher badge feature.  I needed to create an api endpoint to consume the voucher count, because we use services.

Unused Voucher Badge
The first iteration, the api passed voucher objects. However, I decided to refactor and pass the voucher count and less data between services, in hopes to shave couple milliseconds off load time. My first refactor looked like the following. Looking at the code, I know exactly what is happening and what data is being passed. It’s simple. 
unused voucher count as integer
Now imagine you’re consuming this new endpoint for the first time, with no knowledge of the implementation. You receive the following json object.  You might think, “Seven? what the hell does seven mean?” Then you’ll inevitably follow the source to VoucherController in order to understand.   

Instead, why not be explicit? Make it clear to everyone who consumes your lovely api what their getting back.

unused voucher count as a hash

That is so much better! Now I don’t have to visit the Api and look at the source code in order to understand what the object is.

So why not make others happy by telling them what their consuming, instead of making them think. Make your api intention explicit!
[EDIT]

Lets take this one step further and change our controller name as well. To be clear the vouchers controller only had one job of grabbing all unused voucher and returning the count. If it was part of a larger vouchers controller, I would have refactor it out instead .


Not only are we explicit with the object that is being returned, but the API name. I think this gives a better explanation of the api.

This post was based on a lightning talk I planned on giving at RubyConf 2014. However, the talks were cut short so I decided to make a blog post about it instead.

Standard

Using Ruby memoization

In a service oriented architecture we make many requests to external services. These requests time adds up and slows down the site. To improve our performance I can use the memoization technique to make our pages faster.

memoization is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.

When I program I memoize by setting an instance variable to a request.

  1. @foo || = bar.new(params)

This makes a request once, then sets the value to @foo. This persists as long as the instance of the class.

Recently I faced a problem where a method was being called twice. We’ll call it external_request? method. I pass in foo and bar, then check to see if it returns true. The issue was the method was being called twice. The first call returned true, but the second call would return false.


  1. def external_request?(foo, bar)
  2.     @foo = foo
  3.     @bar = bar
  4.     @foo && @bar
  5. end

 Because I care about the first call I knew I could solve this by memoizing the values.

  1. def external_request?(foo, bar)
  2.     @foo ||= foo
  3.     @bar ||= bar
  4.     @foo && @bar
  5. end

This returned true even after the second call, because I saved the value into the instance variable.  Now something to keep in mind. When you are using this technique you want to be careful not to memoize a class variable.

The danger is that this class variable is shared between all users who are on the same instance(unicorn, dyno, etc). Can you see the problem there? If user A makes a request and returns true, then every time the method is called this will return true even if it should be false for other users. This will persist until the unicorn is switched or we refresh the server.

How did I solve the above problem? By making a new method that calls external_request? and sets a cookie during the first method call.

  1. def make_request
  2.   if self.external_request?(foo, bar)
  3.     cookie[:external_request] = 1
  4.   else
  5.     true
  6.   end
  7. end
  8. def self.external_request?(foo, bar)
  9.   foo && bar
  10. end

The lesson is that memoization is great for performance, but we need to be careful what we memoize. In almost every case you don’t want to memoize the class.

This post was inspired by Jon Phillips.

Standard