Want to Build a Better Web API? Build a Client Library!

A solid web API can be an important thing to have. Not only is it great to give users direct access to their data, but exposing data and operations via a web API enables your users to help themselves when it comes to building functionality that doesn’t really make sense in the application itself (or functionality that you never really thought of). It’s also a great way for users to get more familiar with your service.

However, if your API sucks, you can rest assured that nobody will touch it. We’ve all had to deal with crappy web APIs, the ones that make you jump through hoops in order to perform a task that should be dead simple to do. Web APIs should make the simple tasks easy, and the hard tasks possible. To add to the challenge, APIs are notoriously difficult to change. Even with a solid versioning scheme, it is often a real chore to get your users to stop using the deprecated API in favor of the new version. So, it’s important to do a good job the first time.

When building a web API, identifying the tasks that one might want to perform can sometimes be difficult to see when you’re surrounded by JSON, XML, GETs, POSTs, PUTs, DELETEs, and HTTP status codes. While it can be easy to see what single actions you would want to expose, seeing how those actions may interact with each other can be much more difficult. Sometimes you need to take a step back, away from the land of HTTP, in order to see your API as another programmer would see it.

Building a client library that wraps your web API is a great way to do this. It’s relatively easy to imagine how your requests and responses could be represented as objects. The largest benefit of this exercise is to take it a step further, and give the user of your client library the ability to determine what they should do next. Simply knowing if an API call succeeded or failed is usually not enough. Users of your client library need to be able to determine why the request failed, and understand what they can do about it. This extends well beyond the lifecycle of a single HTTP request and response.

Communicating errors

There are several different ways to communicate errors to the user. The proper use of HTTP status codes is one such way. The 4xx class of status codes are specifically intended to be used to communicate that something was wrong with the client’s request. If your API methods are simple, and specific in their purpose, you may be able to rely on HTTP status codes alone to communicate the various causes of failure to the client.

If your API method is complex, and could result in many different failure scenarios, you should first try to break it down into smaller, more specific API methods :) If that can’t be done, then another option is to return some easily parseable text in the response body (JSON or XML) that includes an error code that identifies the specific failure scenario. The response body could be as simple as:

{ "error_code" : 123 }

You could also provide a description of the error in the response as well. This helps users getting started with the API, saving them from having to constantly refer to your API’s documentation every time they get an error:

{ 
  "error_code" : 123,
  "error_message" : "A widget with that name already exists"
}

The important thing is that all failure cases be easily identifiable via a specific, documented code (HTTP status code or custom error code). Error messages should be seen as purely supplemental information. At no point should your users have to parse the error message to determine what happened, or what they should do next.

Isn’t this the same as “dogfooding”

Not exactly. Dogfooding simply involves using what you have created. You could easily dogfood your web API by firing HTTP requests at it using a simple HTTP client library. It is not until you need to take different actions based on different responses that you really start to see if you are properly communicating the result of the request. Building a client helps with this because it forces you to think about the different results and error scenarios in order to decide how your client should handle them. Which failures should raise exceptions? What sort of exception should be raised? How should non exceptional failures be communicated to the caller?

The next step in this process would be to build an application that uses your client library. That step could help identify issues with your client library, just like building the client library helps identify issues with your web API.

The client library

Oh, and don’t forget. At the end of the day, you’ll end up with a better designed web API, AND a great client library that your users can use to interact with your system. Not a bad deal!

Professionals Act Professional

I’m sick of it.

Every week (at least it feels that way) some new drama rears its ugly head in the ruby community. Petty arguments via twitter, one ranting blog post after another, people mocking ideas they consider less than ideal, and even some personal attacks thrown in the mix. There’s so much drama in fact that there is now a site out there that lists it all for the rest of the world to see.

Seriously? Are we all still in junior high?

Just think for a minute about all of the time and energy we are wasting here. Instead of igniting these flame wars, from which nothing productive is ever achieved, we could be growing as a community. We could be bringing up the next generation of software developers. We could be positively encouraging others to build better software. We could be sharing our experiences with others. We could be leading by example.

For a community of people complaining that they’re not treated like professionals, we sure don’t act very professional. If this is the way we behave, can we honestly expect people to treat us with the respect that they treat doctors, accountants, teachers, and members of other professions?

If you want to be treated like a professional, it’s best to start acting like one first.

Take the high road for once. The view is much nicer.

ReloadablePath in Rails 3

A core feature of the Signal application is support for custom promotion web forms. Custom promotion web forms allow our customers to create custom web pages that will allow their customers to interact with their promotions via the web. Our customers currently use these web forms for sweepstakes entry, email/SMS subscription list opt-ins, online polls, and more.

One of the best things about custom promotion web forms is that it allows our customers to completely control what the web page looks like. The Signal application allows for the creation of a web form theme that can be used as a template for a given promotion. The specific promotion can then customize the web page further by specifying the copy that appears on the web page, the data attributes that should be collected, and more.

The web form themes are managed by the Signal application, and are saved to disk as a view (an ERB template) when created or updated. Our customers can edit these themes at any time. When a theme is updated, we need to tell Rails to clear the cache for these specific views, so our customer will see their changes the next time they visit a web page that uses the updated theme.

In Rails 2, this was done using ReloadablePath.

class SomeController < ApplicationController
  prepend_view_path
    ActionView::ReloadableTemplate::ReloadablePath.new(
      "/path/to/my/reloadable/views")
end

However, ReloadablePath is no more in Rails 3. So, we needed to find a new solution to this problem.

Rails 3 introduced the concept of a Resolver, which is responsible for finding, loading, and caching views. Rails 3 also comes with a FileSystemResolver that the framework uses to find and load view templates that are stored on the file system.

FileSystemResolver is very close to what we want. However, we need the ability to clear the view cache whenever one of the web form themes has been updated. Thankfully, this was fairly easy to do by creating a new Resolver that extends FileSystemResolver, which is capable of clearing the view cache if it determines that it needs to be cleared.

Looking at the code for the Resolver class, you can see that it checks the view cache in the find_all method. If it does not have the particular view cached, it will proceed to load it using the proper Resolver. So, we simply have to override find_all to clear the cache if necessary before delegating the work to the super class to find, load, and cache the view.

class ReloadablePathResolver < ActionView::FileSystemResolver

  def initialize
    super("/path/to/my/reloadable/views")
  end

  def find_all(*args)
    clear_cache_if_necessary
    super
  end

  def self.cache_key
    ActiveSupport::Cache.expand_cache_key("updated_at",
      "reloadable_templates")
  end

  private

  def clear_cache_if_necessary
    last_updated = Rails.cache.fetch(ReloadablePathResolver.cache_key) { Time.now }

    if @cache_last_updated.nil? || 
        @cache_last_updated < last_updated
      Rails.logger.info "Reloading reloadable templates"
      clear_cache
      @cache_last_updated = last_updated
    end
  end

end

Since we're running multiple processes in production, we need a way to signal all processes that their view caches should be cleared. So, we're using memcache to store the time that the web form themes were last updated. Each process then checks that timestamp against the time that particular process last updated its cache. If the timestamp in memcache is more recent, then the ReloadablePathResolver will clear the cache using the clear_cache method it inherited from Resolver.

Next, we need to add some code that will update memcache any time a web form theme has been updated and saved to disk.

class WebFormTheme < ActiveRecord::Base
  after_save :update_cache_timestamp

  private

  def update_cache_timestamp
    Rails.cache.write(ReloadablePathResolver.cache_key, Time.now)
  end
end

The final step is to simply prepend the view path with the new ReloadablePathResolver.

class SomeController < ApplicationController
  prepend_view_path ReloadablePathResolver.new
end

References

  • Beware the Hack!

    Hacks are dangerous little creatures. They live in the darkest, dustiest corners of your application, forgotten about, waiting… Waiting for the chance to rear their ugly little heads, open their disease infested mouths, and sink their jagged teeth into customer confidence and developer productivity.

    We’ve all been there. We have a product that works great. It solves a certain problem incredibly well. Then, a well meaning customer comes along and says, “This is fantastic. It almost solves my problem perfectly. Is there any way you can modify it slightly to do X instead of Y.”

    Sometimes this is no problem at all. Sometimes the design of the product is flexible in ways that make it a breeze to add this functionality. But, sometimes the request comes out of left field, and takes your product in a direction you never anticipated. While we strive to build software that is extensible and adaptable (it is software after all, isn’t it?), none of us can see the future, nor anticipate every possible customer request.

    About this time you start to hear a little voice inside your head. “Well, I suppose I can hard code this, or add an if statement here, or write a one-off script to do X”. After all, you don’t want to tell your customer “No, sorry, we can’t do that”. And, they certainly don’t want to hear “Sure, we can make that modification, but it will require a significant amount of refactoring in order to ‘do it right’”.

    Acting on these thoughts births a tiny baby Hack. The Hack is little when it’s born, but it certainly doesn’t stay that way. Once the Hack is born, it is much easier to add to the Hack, or feed it. With everybody modification to the Hack, it gets bigger, and bigger. Pretty soon you have a large, ugly Hack with a nasty attitude on your hands. And, despite you being it’s “mommy”, it doesn’t like you, at all. Not one bit.

    Hacks are dangerous for several reasons.

    First, they almost always live outside the main execution path of the code. This means they’re not executed nearly as often as the other code. Even if you have a series of tests for the Hack, nothing exercises code like constant execution by your customers. Also, because they’re not really “part of the application”, Hacks are often forgotten about when updating or fixing code.

    Second, they’re usually created to quickly get around some issue. And by “quickly”, I mean “didn’t totally think this though, but I’m fairly certain that if I tweak X, alter Y, and drive it with a custom script, it should work just fine”. And, usually it does work just fine…at least in the beginning. But, this is when the Hack is still young, and under your control. Adult Hacks are not nearly as cooperative.

    Third, they’re usually only known about (at least in detail) by the members of the team that created them. A Hack is like a big, puss filled pimple on your ass. You don’t go around showing those to your friends and co-workers, do you? Hacks, by definition, are quick and dirty solutions to problems. They’re not elegant, or sexy. So, developers tend to keep their Hacks to themselves. At most, a developer will mention that they hacked around a problem, but rarely do they go into details. The other team members are largely left in the dark. Not knowing where a Hack lives or how it behaves is a sure fire way to get bitten by it down the road.

    Always remember, that little baby Hack…it will grow up. It will get nasty. It will bite. It’s just a matter of when and where. Those who have been programming for long enough know this to be fact. And, being nasty little creatures, Hacks usually wait until the worst possible time to bite.

    So, beware the Hack! They are big, ugly, mean, have teeth, and will most certainly bite.

    Introducing Proby – Task monitoring made simple!

    Note: This entry has been cross-posted from the Signal company blog.



    One Monday morning about a month ago, I was browsing through open issues in our bug tracker looking for something to work on. It was my week on “technical support”. At Signal, the engineer on technical support spends a week working on non customer facing issues with our infrastructure, process, etc, that are too large to simply knock out as you encounter them.

    An issue entitled “Add monitoring to detect jobs that do not start” caught my attention. We already had monitoring in place to alert us when a job fails. However, we had nothing to alert us if that job never starts in the first place. We have many scheduled jobs that run in the background, some pretty important, and have been bitten on more than one occasion by reoccurring jobs that had not run for quite some time.

    I bounced some ideas off the team, and we all agreed that that it made sense to create a new application to monitor our scheduled tasks. The app would know what is supposed to run, and when. We would change all of our jobs to “ping” the app when they start and finish. If the app detects that something did not run when it was supposed to, or did not finish when it was supposed to, it could alert the team.

    And just like that, Proby was born.

    “What’s with the name?”, you ask? I likened the application to a probation officer, keeping an eye on wayward tasks.

    Since going live, Proby has caught several issues with our scheduled jobs that would have gone unnoticed…many more than I would have anticipated. It has also evolved into a fairly complete application. It is easy to setup new tasks. It holds onto the execution history of each task, allowing you to see a trend of the run time for each task, and which runs resulted in an alarm. It supports alarms via email or SMS, using Signal’s SMS messaging API. It has several settings that allow you to tweak exactly when an alarm is sent, cutting down on the number of false alarms. And, thanks to designer extraordinaire Drew Myler, it looks amazing!

    Shortly after creating Proby, we realized that there is no way we are the only ones who have had scheduled tasks go days without running, unnoticed. So today, we are making Proby available to the general public. It is currently in closed beta, and we are letting people in slowly to flush out any bugs or other issues. Use of Proby is also FREE while it is in beta.

    Interested? Head on over to http://probyapp.com/signup and signup to participate in the closed beta! And, don’t forget to follow @probyapp on Twitter for updates!