There is one mistake I have seen repeated over and over (and over…) in applications that work across the network – from the 1980′s up through the present. The symptom is an application that works fine in testing, but performs very poorly when deployed. The cause is developers who fail to use the network efficiently.
The example I first saw was when at FileNet in the 1980′s, and we hooked up a client workstation over a 56Kb/s connection. The drop in performance was much greater than the difference in data rate with the 10Mb/s local area network. Accounting for the difference was initially a puzzle.
Networks have both overhead and latency. Latency is time it takes for a message to travel across the network. When the test server is on the same wire as the test client – the usual case in development – latency is quite small, can be and (by mistake) frequently is ignored by developers. At this stage of development an application that passes many messages across the network can perform well. Developers tend to a design nice uniform network API – much like the interface to a local library. With an API of this sort, client startup might take hundreds or thousands of messages sent across the network.
With a simple PING test, I see ~0.01ms for a round-trip to my local server, ~10ms to my ISP’s local mail server, and ~100ms to most major sites on the web. The latency to cross the web is about a thousand times the latency on the local network. The latency to cross my ISP’s intranet is about a hundred times the local latency.
Note that latency is essentially unaffected by the speed of your network connection or your servers. If latency is a problem, acquiring a faster network is unlikely to help. A faster network connection is really better described as a higher capacity network, as the you can send more bits per second, but the bits don’t cross the network any faster.
At FileNet the 56Kb/s link went off-site, and had latency not very different from a web connection today.
The problem comes when the application is deployed, and the server is no longer local. A hundred calls across the network cost the application perhaps 1ms of latency in developer testing (seldom a concern). A hundred calls across the web could easy add 10 seconds, and make an application noticably slow. On the local network you could make hundreds or even thousands of calls without noticing a drop in performance. Move the same application to the web (or a company WAN) and performance becomes painfully slow. Once the problem hits you in the face (1980′s in my case), the answer should be obvious. For optimal performance you want one call across the network for one user action. Program startup? One network call. User clicks a button? One network call.
You must not design a network API the same way you would design a local library.
Which leads us to REST. I very much like the idea of using HTTP without any unnecessary added abstractions. I like the idea of stateless interaction. When writing client/server applications it became clear that using stateful connections were as much a problem as an aid – even in the 1980′s. What I find disturbing is the repeated reference to a uniform interface, and reference to using GET / PUT / POST / DELETE methods.
Put simply, a REST API should be asymmetric. For minimal latency, the oft-used GET calls should return a collection of objects. Since changes usually cause side-effects, the oft-used POST calls should return a collection of objects. Latency matters. To avoid the risk of new (and not so new) programmers repeating old mistakes, we need to offer a model that accounts for latency. A good network API should by design aim for one request/response per user action.
Note also that there is no point in trying to re-use the HTTP method (i.e. GET, PUT, POST, DELETE, etc.) as a verb, since most applications need a somewhat greater vocabulary. Shoehorning your application into the HTTP verbs is probably a bad idea. Better to stick to GET for read access, and use POST for changes. Note that using fewer messages to minimize latency also helps an application to scale. The OS and web server overhead for handling requests is pretty much fixed per message. Fewer messages mean less overhead, and more capacity available to your application.
If you take latency into account, then the REST model becomes:
- GET collection of nouns
returns response: collection of objects.
- POST collection of actions (where an action is a verb and a collection of nouns)
returns response: collection of objects.
The REST model is a step forward, and better with an emphasis on asymmetry and collections.