Hashbang and pushState
Published by Scott
A while ago, Google made a suggestion for crawling websites that used AJAX for navigation. Soon after, Google implemented the idea in their search engine. Few paid attention to this until last September, when Twitter started using the technique. Then, last week, Gizmodo adopted the technique, and the internets promptly exploded.
The technique, known as "hashbang," relies on URLs of the form http://example.com/#!path/to/content. Opinions on hashbangs are varied, in that some people think it's bad while others think it is terrible, no good, or very bad. There is widespread agreement this isn't how the web is supposed to work, and strong evidence that it really doesn't work at all, beyond a very narrow definition of "works." Simon Willison has collected several articles under a tag of "hashbanghell".
Without rehashing all of that, let's simply agree that hashbangs are an affront to all that is good and decent on the web and those pushing this technique are, as Tim Bray put it, "beating the Web to a bloody pulp with a sharp-edged hashbang." With that agreement, let's move on to learning something from this experience, something more interesting than "hashbangs are bad."
Why do bad things happen to good URLs?
If hashbangs are so terrible, why are people using them? Mike Davies says "Out of all the reasons, the strongest one is 'Because it’s cool'." Tim Bray doesn't find it cool, dismissing it as "dynamic AJAXy magic". Jeremy Keith doesn't seem to even recognize that others might find hashbangs cool, saying "I’m so surprised that any front-end engineer would knowingly choose to swap out a solid declarative foundation like HTML."
Words like "cool" or "magic" are dismissive of the benefits of hashbangs. This may be a rhetorical device from the "considered harmful" school of blog posts, in an attempt to shame the web away from every using hashbangs again. Or maybe people really don't recognize hashbangs as doing anything remotely good. Either way, we should look the real benefits of hashbangs: for most people, hashbangs improve usability.
for most people, hashbangs improve usability
Hashbangs reduce two of the most fundamental usability issues with web interfaces: responsiveness and context. The web is made of links, and every link we click causes a full page refresh. We have to wait for that page to refresh (the responsiveness problem) and while we wait, we have a visual gap between the previous page and the next page (the context problem). Interfaces in our native applications don't generally have these problems. They can smoothly and quickly transition between contexts in ways that don't require users to re-orient themselves at all. As web professionals, we're all experienced enough that we've learned to ignore these problems, but they're still usability issues. Hashbangs greatly reduce these negative aspects of page reloads by removing page reloads. For most people, that's a usability improvement.
For most people
The fundamental problem with hashbangs is simply that the web was never intended to be usable by most people. Pretty much every part of the web is built to be fault-tolerant, so if something doesn't work for even a small minority of users, the rest of the web does work, and the general experience is still usable. Hashbangs improve usability for most people, but completely break usability for others. See the articles above for a description of specifically how hashbangs break the web. The question we should be asking, once we've recognized that hashbangs improve usability for most people, is how can we gain those usability improvements without breaking the web?
Twitter comes close to doing hashbangs well. If you're using a system that doesn't support hashbangs, you'll generally never see them on Twitter. You'll instead get a fully-functional Twitter experience with the old, non-hashbang URLs. Gizmodo does this part completely wrong, completely disabling the old URLs. I wonder if Gizmodo might have gotten away using hashbangs had they employed progressive enhancement as well as Twitter did. But even though Twitter works well for an individual non-hashbang user, that's not good enough because the web is a social environment, people pass URLs around. If my browser doesn't work with hashbangs, and you send me a hashbang URL, it won't work for me. This is especially problematic for Twitter, as passing around URLs is a large part of what Twitter does.
So how do we solve this problem? It's a complicated problem, and I couldn't think of a good solution. So I'm happy HTML5 has already solved it for us. <a href="http://badassjs.com/post/840846392/location-hash-is-dead-long-live-html5-pushstate">window.history.pushState()</a> allows sites to change the URL in a browser without a page refresh. Because pushState is using standard URLs, this gives us all the usability improvement of hashbangs, with none of the breakage. Everyone wins.
So yes, hashbangs are evil, but they're evil in the service of usability, and usability is good. Rather than tossing out that usability improvement, we should be pushing sites to improve usability the right way, with pushState.


"Hashbangs improve usability for most people, but completely break usability for others"
Who are they?
"If you're using a system that doesn't support hashbangs, you'll generally never see them on Twitter"
What systems?
We can run Single Page Interface web sites in a myriad of browsers and devices, including JavaScript disabled.
Take a look to The Singe Page Interface Manifesto
jmarranz, I think those I linked did a good enough job of explaining the problems with hashbangs, so I didn't bother repeating them, but in short, the problem is exactly what you mentioned: JavaScript disabled, either intentionally or because there's a JS error somewhere.
And as I mentioned above, people with JS enabled send URLs to people with JS disabled. So only exposing the JS-only URLs to people with working JS is better than exposing them to everyone, but it isn't a full solution. You're still indirectly exposing those URLs to people who can't use them. I don't see any solution to that problem in the article you linked. pushState solves that problem by only ever exposing URLs that work for everyone.
I agree, hashbang URLs do not work as bookmarks if JS is disabled, in this case hashbang is ignored and usually the default page is loaded.
I think this problem could be fixed with some kind of browser plugin automatically changing the URL to the form escaped_fragment when JS is disabled.
You may be interested to note that the World Wide Web Technical Architecture Group (TAG) has published a formal finding discussing the tradeoffs involved in using fragment identifiers to identify application states. It recommends the use of pushState with non-# URIs when possible.
See: http://www.w3.org/2001/tag/doc/IdentifyingApplicationState
Noah