at pycon the other week, Jonah and i gave a lightning talk on tasty and some of the ideas we've been thinking about in the area of "mini apps". a lightning talk is a super fast 5 minute presentation. the idea is that a lot of people can get an idea or two each out in front of a whole conference without much stress or prep work. in particular, people tend to give lightning talks to show off a cool little trick or to just put out an idea that might not be fully formed yet and get some feedback early on. that was pretty much why we gave one.
it occurred to me though that i should probably write up the basics of the talk and post it here partly to disseminate the ideas a little more and partly because writing it forces me to clarify some of my own thinking on the subject.
so this post will basically cover the material that Jonah and i raced through in our five minutes at pycon with a bit more elaboration and refinement.
our talk was titled "Using the Web as Your Application Platform" but could also really be thought of as "Taking REST to Extremes". none of the ideas we've been exploring are particularly novel or cutting edge on their own, but i think we've been pushing things a bit further than most.
everything i've been building lately has been very strongly guided by the REST architectural principles. i drank as much of that particular kool-aid as i could find. the main concept behind REST is basically designing your application to take full advantage of HTTP and the inherent architecture of the web, which is much more thoughtfully designed than many people realize.
like most developers, i've been chasing the pipe dream of "reusable components" for a long time. every programming shop out there at some point dreams of creating a library of reusable components specific to their problem domain that they can just put together in different ways to quickly tackle any new projects that come along and never have to write the same thing more than once. there are a million different ways that we attempt to build our little programming utopia: libraries, frameworks, component architectures, fancy content management systems, etc. every hot new language promises to make this easier and every vendor bases their sales pitch around it. most of the time though, every solution comes up a little short in one way or another. with deadlines and annoying real world requirements and restraints, the utopia never quite seems to materialize.
eg, where i work, we develop in python, perl, java, and php and we use a number of frameworks. some of those because they're productive and nice to work with, some because of legacy reasons, some because of the particular environment that an application has to run in, and some because they are local customizations of open source applications and we can't exactly change the language they were implemented in to suit our whims. i don't think this situation is all that uncommon for web shops. right away, this is clearly a huge limitation on how much reusability we can get out of a library or framework. if i write something in python it isn't going to be much use in java or php. so for a common task, like authenticating against the university's central system, we've got to implement it at least four times, once for each language.
REST offers a solution for this particular predicament though. every language out there and every framework that we might reasonably use comes with built in support for HTTP. if you build a small self-contained application where you might have written a library or a component, it's immediately usable from all the other languages and frameworks.
this was the reasoning behind tasty's implementation as an application with a REST API instead of just as a library. tasty was written in python with turbogears which is my current high productivity environment but it was written as part of a project which we have to eventually deploy in a java course management system and which we're also doing UI prototyping for in Plone. to be blunt, i just can't stand java. implementing a full featured tagging library in java on a j2ee stack would have taken me months and probably driven me nuts. i could have built it on Plone; that would have been reasonable, but it still would have taken me a bit longer and i would have had to wrestle with converting the relational model that tasty uses to an the object database that Plone uses and it probably would have ended up buggy or slow and my head might have exploded.
tasty isn't particularly unique either. i've been writing applications that are designed to be tied together for a while now and i've got a small stable of them that i can and do mash together to build more complex applications.
this weblog is a good example. the main application is python and turbogears but it ties together at least four mini applications behind the scenes. full text search is handled by a seperate application. when you type something into the searchbox here it makes an HTTP request to the search application behind the scenes. when you view any post, it makes an HTTP request to a tasty server to get the tags for that post. if you log in to the admin interface, it uses a seperate application that does nothing but manage sessions. if you upload an image, it passes the image off via an HTTP POST request to a seperate application that replies with a thumbnailed version of the image. and i've only started converting to this strategy. i plan on extracting a lot more of the functionality out into seperate applications before i'm through. eg, i think that threaded comments can be their own application, i have an application that does just markdown/textile/re-structured text conversion that i plan on plugging in, templating and skinning can be a seperate application, and i've got a nifty event dispatching application that opens up a number of potential doors.
one good example i can bring out to demonstrate just how far we're taking this concept is a tiny little internal application that we call "pita". pita implements "data pockets". essentially, it's an HTTP accessible hashtable. you can stick some data in at a particular 'key' using an HTTP POST request and then you can pull that data out with a 'GET' request to the same url.
using curl on the commandline, a sample session would look like this (assuming that pita.example.com was running a pita server):
% curl -X POST -F "value=this is some data to store" http://pita.example.com/service/example/item/foo % curl -X POST -F "value=this is some more data" http://pita.example.com/service/example/item/bar % curl http://pita.example.com/service/example/item/foo this is some data to store % curl http://pita.example.com/service/example/item/bar this is some more data % curl -X DELETE http://pita.example.com/service/example/item/foo % curl -X DELETE http://pita.example.com/service/example/item/bar
it's a fairly trivial application, but it comes in quite useful, particularly if you want to share a little data between multiple applications. the applications sharing data don't have to all have access to the same database, they just have to all point at the same pita server and agree on a convention for naming keys.
doing the above sequence of pita operations from within a python session is fairly trivial too using my little restclient library:
>>> from restclient import POST, DELETE, GET >>> base = "http://pita.example.com/service/example" >>> POST(base + "/item/foo", params=dict(value="this is some data to store")) 'ok' >>> POST(base + "/item/bar", params=dict(value="this is some more data") >>> print GET(base + "/item/foo") this is some data to store >>> print GET(base + "/item/bar") this is some more data >>> DELETE(base + "/item/foo") >>> DELETE(base + "/item/bar")
using JSON as the format for the payload (or occasionally XML for some of the really tough stuff) makes exchanging more complex structured data very easy.
hopefully by now it's clear that we're really following through on the promise of the title of the talk. the web itself is our application platform, framework, or component framework (depending on how you look at it). it doesn't matter much what each of the components are built out of, as long as they speak the same language to each other (which in our case is the language of the web: HTTP).
aside from the language independence win (which is certainly not insignificant for us, although it might not impress developers who stick to a single platform as much), this approach has a number of other nice qualities.
first, it positively forces you to design components that are loosely coupled and functionally cohesive; two primary goals of software design. it's one thing to hide an implementation behind a library API, it's quite another when the abstraction is so tight that a component could be swapped out with one written in a completely different language and running on a different physical computer and the other components would never be any the wiser.
second, it actually scales out really well. one of the first responses i usually get when i start explaining what we're doing to other programmers is "but isn't doing a bunch of HTTP requests all over the place really slow?". that question sort of annoys me on the grounds that they're implicitly optimizing prematurely (and many of you know by now how strongly i'm against premature optimization). yes, making an HTTP request is slower than a plain function invocation. however, in practice it isn't so much slower that it actually makes much of any difference that approaches being a bottleneck. there are some good reasons for this. first, the nature of how these things tie together usually means only a couple of backchannel HTTP requests to each mini app involved on a page view; not hundreds or thousands. an HTTP request might be slower than calling a function from a library, but only by a few milliseconds. so using this approach will add a few milliseconds to the time on each page view, but that's pretty negligable. remember, most significant differences in performance are due to big O algorithm stuff, not differences of milliseconds on operations that aren't performed very often. a second argument is that these sorts of components are almost always wrapping a database call or two. while an HTTP request might be much slower than a function call, it's not much more overhead at all compared to a database hit. i've done some informal benchmarks against mini apps running locally (i'm certainly not advocating that you spread your components out all over the web and introduce huge latencies between them; there should be a very fast connection between all your components) and the overhead of an HTTP request to local cherrypy or mod_python servers is pretty comparable to the overhead of a database hit. so if you're comfortable making an extra database request or two on a page, hitting an external app shouldn't be much different.
one of the most powerful arguments against the performance naysayers though is the scalability argument. yes, there's a small performance hit doing this, but the tradeoff is that you can scale it out ridiculously easily. eg, if thraxil.org all of a sudden started getting massive amounts of traffic and couldn't handle the load, i could easily setup four more boxes and move the tasty server, the full-text search server, and image resizer, and the session manager out to their own machines. the only thing that would change in the main application would be a couple urls in the config file (or, with the right level of DNS control, that wouldn't even be necessary). the hardware for each application could be optimized for each (eg, some applications would get more out of lots of memory while others are CPU or disk bound). plus, there exist a myriad of solutions and techniques for load balancing, caching, and fail-over for HTTP servers so you pretty much get all that for free. instead of implementing caching in an application, you can just stick a caching proxy in front of it.
scaling out to lots of small, cheap boxes like that is a proven technique. it's how google builds their stuff (they use proprietary RPC protocols instead of HTTP, but the idea is the same). large parts of the J2EE stack exist to allow J2EE applications to scale out transparently (IMO, it takes an overly complex approach to scaling, but again, the core idea is basically the same). you can think of the web itself as a really large, distributed application. REST is its architecture and it's how it scales. so, arguably, this approach is the most proven, solid, tested, and well understood approach to scaling that's ever been invented.
i really only cover the scalability thing though as a counter to the knee-jerk premature optimization reflex. i mostly view it as a pleasant side effect of an architecture that i'm mostly interested in because of its other nice attributes. even if it didn't scale nearly as well, for me and probably the lower 80% of web developers who don't need to handle massive amounts of traffic and for whom maintainability, loose coupling, and reusability trump performance it would still be an approach worth trying. luckily though, it appears to be a win-win situation and no such tradeoff needs to be made.
i have a ton of other things that i'd like to say about this stuff. thoughts on different approaches to designing these mini applications, how the erlang OTP was a large inspiration to me, tricks and techniques for building, deploying and using them, recommendations for squeezing even more performance out of them and avoiding a couple common pitfalls, ideas for frameworks and libraries that could make this stuff even easier, and even one idea about using WSGI to get the best of both worlds. but i've already covered a lot more here than was in the lightning talk so i'll save those ideas for future posts.
the only other thing which i do want to note and that i didn't get a chance to mention at pycon was Ian Bicking's posts on the subject of small apps instead of frameworks. Ian, i think, has been thinking about some of the same stuff we have. i was building these small apps (of a sort) before i read his post, but i hadn't really thought much of it. coming across Ian's posts made me a lot more conscious of what i was doing and helped me crystalize some of my thoughts and really focus my development and experimentation.
anyway, expect more from me on this subject. and hopefully, by next year's pycon, we'll have enough nice demos, benchmarks, and experience building these things that we can give a proper full talk then (or actually at just about any other web/programming conference, since these ideas aren't really python specific at all).