garann means > totes profesh

a defunct web development blog

a healthy level of distrust

Thu, 04 Nov 2010 00:24:23 +0000

In the time before XHRs, you used to submit a request to a server and then wait while some icon on the toolbar of your browser spun around to see that your request had been received. Your page would refresh and there would (hopefully) be some indication that the action you'd taken had the result you intended. This was slow and annoying and is probably why XHRs are so popular today. The whole page had to redraw before you could get anything done, even if the bulk of the page had nothing to do with what was actually changed when you clicked the link or button. Inventive people would add things like site-wide chat to their sidebars and that would be awesome except that knowing what had transpired in the conversation meant refreshing the page. So now, of course, we don't do that.

I'm pretty out of touch with backend stuff these days, so it surprised me to find out that asynchronous database interaction is now A Thing. We implemented this at work recently, or, more accurately, were informed that it had been implemented some time ago. As someone who works entirely in an event-driven medium, I probably should have reacted to this news with "OMG that is soooo awesome," but my reaction was the opposite. Why? Cause when I tell the server to do something I want to know it's done it. Knowing that it intends to do it is not helpful to me.

My mental picture of a web application has the frontend as the super-attentive waiter who makes you feel welcome, shows you where to sit, brings you water before you even knew you were thirsty, but sometimes forgets that you asked for french fries instead of a salad. It has the backend as the ultra-serious chef who took the food handler's certification three times after passing the first, just to be sure the material had really sunk in, and is kind of slow sometimes but carries out all chefly duties as though disabling a bomb. I like it that way. I think that's good. Frontends are allowed to be a little flaky. Backends are allowed to be a little slow. They work together to make sure the user always gets the best result, and when one falters the other makes up for it.

A backend that's going to get around to writing to the database next time it's convenient does not fit my mental picture and therefore I regard it with suspicion. I realize these transactions take seconds and are going to succeed almost 100% of the time. But handling data transactions asynchronously means that all the server's communication with me has an additional "*probably" appended to it, and I don't like that. The frontend relies on conjecture. We repaint at will, we blur the lines between preview and autosave, we progressively enhance and introduce fallback after fallback to handle the exceptions when something doesn't go as planned because we exist in the world of the user's computer. There are things we just can't control in that space so we guess and we try twelve different methods if necessary to provide the functionality the user needs. When we check in with the backend, we're supposed to get a reality check, a reset where yes, ok, everything is once again certain and concrete. The information we receive is not a manipulable user agent string but the JSON output of some API we know intimately. Without that, things begin to feel uncomfortably chaotic.

I actually have a concrete example, not just a bunch of vague fear-mongering. A month or so ago there was an issue with twitter that had to do with following people and adding them to lists. I tried to follow/add several people from the All-Girl Hack Night I-don't-know-how-many times, and got the same result over and over: the button would be redrawn to indicate I'd taken an action and the person would nonetheless not appear in the list of people I was following. Something obviously went south after the frontend fired off its request and the backend responded favorably. It may have nothing to do with asynchronous communication on the server, but it's the same sort of problem. The server said yes when it meant no, and since it's a server, it should panic, lock all its doors, and call in tanks when it means no. Relaying that information more gently to the user is the frontend's problem, but the backend should freak the fuck out when something goes wrong.

So the issue with this async stuff is that there's the chance for the backend to do its irrational (but necessary) freakout thing once the frontend is no longer listening. The frontend goes along thinking everything is fine when it is so not. Communication breaks down. The user's shit gets lost somewhere in the aether of the internets. Nasty emails are written. Chaos ensues.

To get around this, we've decided to start polling for things. Initially we were like, "Oh we're just going to poll for important stuff like form entries, not little things like votes or follows." But you know what? It's all important. Every click represents a second a user remained on our site, interacted with our system, gave us feedback, and trusted us to hold up our end of the bargain. So fuck it, we're going to poll for everything.

What I mean by polling is that we submit the XHR like normal, but the success callback doesn't redraw the screen. It may throw up a little "please wait" indicator, but we don't pretend anything has actually happened. What we do instead is begin calling a different method on the server at regular intervals asking if the user's action is ready yet. Only once we find the object or response we're looking for do we quit polling and redraw.

And now you are going to say, "What the fuck is this, 1996? The user has to sit there and wait while the backend processes their stuff?" Your criticism is half-right. Yeah, we're exposing the non-magic-ness of the website and reminding them that the connection on their newfangly mobile device is still really shitty even though it costs $150/month. But they're not completely blocked. They still have access to the rest of the page and can do interesting stuff or, I don't know, look at ads. In our application, they can't actually leave the page until the polling is done and the result appears, but like everything else under the sun this becomes a whole order of magnitude cooler in a single-page app where processing can continue "in the background" and the user can pile up requests all damn day if it makes them happy. I think it's a fair compromise because, whether they realize they're entitled to it or not, they get more certainty than if we were to instantly redraw every time they took an action when it's possible the database may have been eaten by lions and their action was never recorded.

We spend a whole lot of time making shit flashy and making it feel fast. That's good, it provides a more pleasant user experience. But it should never be disingenuous. Sometimes it takes a second or two for an operation to complete. Dealing with that in a friendly way is up to those of us on the frontend, and it's no different than handling errors or providing a way to Undo. It's pretty cool, honestly, that servers can publish and subscribe asynchronously. But I hate to think of developers (any kind) getting so carried away by cool technology that we forget our primary responsibility to make shit work as close to 100% of the time as possible. Therefore I think the best way to deal with a backend that's becoming more like a frontend is to treat it with the same caution we would any aspect of the frontend and wait to message users about things until we're good and sure.