Sunday, April 18th, 2010

Facebook’s Javascript Speed-Up

Category: Performance, Showcase

<>p>facebook_logo

(Live blogging notes.)

Makinde Adeagbo is describing Facebook’s Javascript performance optimisation efforts at JSConf. By 2009, it became clear something had to be done, as the trend was towards longer and longer page loads. Back in 2006, the objective had been super fast page loading: “If any page loads in under 100ms, it takes way too long”. By 2008, given all the new interactive features on the page, the 100 millisecond target had expanded to 1 second, and by mid-2009, pages were taking 5 seconds to load. Even HipHop, the PHP compiler, was smaller than the Javascript code base. And when Steve Souders called out Facebook in his book numerous times, it only adding to the team’s sense of fun and joy ;).

In June 2009, the team quickly chose a target by year’s end by simply checking the load time without Javascript: 2.5 seconds. Thus, dropping load time from 5 seconds to 2.5 seconds was the goal for end of 2009.

The first initiative was to include the Javascript at the bottom of the page. Great, it’s faster, but at what cost? A big one: Users try to click on controls, and nothing happens. Back to the drawing board, and the team refined the setup so that the actionable stuff was initialised on the top of the page. But how to minimise all this code at the top of the page? Here, the team exploited the observation that most controls work the same way:

* User clicks
* Sends ASync request
* Insert/Replace some content

So the team set up elements like this:

<a href=”/ring.php rel=”dialog”>…</a>

… And then hijacked them with a standard listener routine, one that would work for most of the controls. (Most, not all; 80/20 principle is in effect here.) This way, they could have one small listener routine to handle most of the controls on the page. Once the user clicks, the server gets called and outputs a new snippet of Javascript:

The Javascript:

javascript
< view plain text >
  1. new Dialog().setTitle().setBody().show();

would be output by a PHP script, and then evaluated in the browser:

  1. $response = new DialogResponse();
  2. $response->setTitle()->setBody()->send();

(A form of On-Demand Javascript.)

In fact, the team has a whole PHP library for outputting Javascript. For example, when a request comes in to expand a new story:

  1. $response = new AsyncResponse();
  2. $response->setContent("#elem_id", $new_content);
  3. $response->appendContent("#content .stories", $new_story);
  4. $response->send();

And they are using a convention: “ajaxify=1″ on an element indicates it’s … Ajaxified.

At this point, the team had now Ajaxified a bunch of features, but people were still skeptical about more complicated features. For example, would setting status be too hard with the same techniques. So after some research, Makinde came back with an epiphany: the humble form. Whereas the previous async requests were effectively information-less – just a simple directive and maybe an ID – the requests would now include content too. And of course, most of these things look nothing like forms due to styling. But underneath, they’re still forms, e.g. the entire comments block is a single form.

Nowadays, most of Facebook runs without complete page refreshes, by dynamically flipping the content and the fragment ID. (What Facebook calls page transitions.)

Ongoing, Makinde says performance optimisation requires ongoing vigilance. Every engineer has their special case, but in the scheme of things, it’s better to say no to new features unless they can be strongly justified. For example, we can live with user submitting a form that hasn’t yet been hijacked; a complete page refresh is fine on occasion. We don’t like it, but we don’t want to make it a special case just for the sake of it.

The Gantt charts tell a great tale: users now see content much earlier, and it’s interactive. So how did they fare with that 2.5 second goal for year’s end? Achieved by December 23. And Makinde wants people to know Facebook is hiring as they have more Javascript to write…and delete.

UPDATE: And here are the slides …

Related Content:

3 Comments »

Comments feed TrackBack URI

There are valuable lessons learned here. We always hear about the rule of placing script tags at the bottom of the page but we never hear about the trade-offs. I’d like to read more about what the Facebook JS performance team is doing. Are there any links to some blogs? Yahoo! is notorious for documenting and sharing what they learn. I’d like to see the Facebook folks do the same! Is anyone writing a book? Thanks for the article!

Comment by gancelot — April 19, 2010

This redesign is so big I had to check it myself. During Makinde’s talk I fired up my packet sniffer and confirmed that indeed they’ve gotten all of their scripts out of the way so the page can render more quickly. This is a huge win. While at JSConf I heard similar stories from Ray Morgan (Zappos.com) and Jenn Lukas (Happy Cog) – their sites got dramatically faster by loading scripts after the main content was rendered. It’s not always easy to accomplish, but the results can be significant. Hats off to the Facebook team for not only making this change but stepping forward to share their best practices.

Comment by souders — April 19, 2010

I’m curious if they tried any aysnchronous js loading software like labJS or requireJS. We tried labJS and got into a spiderweb of dependency issues – but Amy Hoy and others speak highly of it.

Comment by AngusC — April 20, 2010

Leave a comment

You must be logged in to post a comment.