Wednesday, April 20, 2011

jquery.com uses only 34% of jQuery

I created a Firefox extension, JsBloat, to help determine what fraction of the jQuery library a web page uses. It leverages JSCoverage to instrument jQuery and keep track of which lines of the library are executed (and how often). The table below is a small sample of sites that I tested with JsBloat. Here, "percentage used" means the fraction of lines of code that were executed in the version of the jQuery library that was loaded:

URL jQuery version % used on page load % used after mousing around
jquery.com 1.4.2 18% 34%
docs.jquery.com/Main_Page 1.4.4 23% 23%
stackoverflow.com 1.5.1 30% 33%
getfirefox.com 1.4.4 19% 23%

Note that three of the four sites exercise new code paths as a result of mousing around the page, so they do not appear to be using pure CSS for their hover effects. For example, on jquery.com, a single mouseover of the "Lightweight Footprint" text causes a mouseover animation that increases the percentage of jQuery used by 11%! Also, when jQuery is loaded initially on stackoverflow.com, it calls $() 61 times, but after mousing around quite a bunch (which only increases the percentage of code used to 33%), the number of times that $() is executed jumps to 9875! (Your results may vary, depending on how many elements you mouse over, but it took less than twenty seconds of mousing for me to achieve my result. See the Postscript below to learn how to run JsBloat on any jQuery-powered page.) Although code coverage is admittedly a coarse metric for this sort of experiment, I still believe that the results are compelling.

I decided to run this test because I was curious about how much jQuery users would stand to gain if they could leverage the Advanced mode of the Closure Compiler to compile their code. JavaScript that is written for Advanced mode (such as the Closure Library) can be compiled so that it is far smaller than its original source because the Closure Compiler will remove code that it determines is unreachable. Therefore, it will only include the lines of JavaScript code that you will actually use, whereas most clients of jQuery appear to be including much more than that.

From these preliminary results, I believe that most sites that use jQuery could considerably reduce the amount of JavaScript that they serve by using Closure. As always, compiling custom JavaScript for every page must be weighed against caching benefits, though I suspect that the Compiler could find a healthy subset of jQuery that is universal to all pages on a particular site.

If you're interested in learning more about using Closure to do magical things to your JavaScript, come find me at Track B of JSConf where I'm going to provide some mind-blowing examples of how to use the with keyword effectively! And if I don't see you at JSConf, then hopefully I'll see you at Google I/O where I'll be talking about JavaScript Programming in the Large with Closure Tools.

Postscript: If you're curious how JsBloat works...

JsBloat works by intercepting requests to ajax.googleapis.com for jQuery and injecting its own instrumented version of jQuery into the response. The instrumented code looks something like:
for (var i = 0, l = insert.length; (i < l); (i++)) {
  _$jscoverage['jquery-1.5.2.js'][5517]++;
  var elems = ((i > 0)? this.clone(true): this).get();
  _$jscoverage['jquery-1.5.2.js'][5518]++;
  (jQuery(insert[i])[original])(elems);
  _$jscoverage['jquery-1.5.2.js'][5519]++;
  ret = ret.concat(elems);
}
_$jscoverage['jquery-1.5.2.js'][5522]++;
return this.pushStack(ret, name, insert.selector);
so that after each line of code is executed, the _$jscoverage global increments its count for the (file, line number) pair. JSCoverage provides a simple HTML interface for inspecting this data, which JsBloat exposes.

You can tell when JsBloat has intercepted a request because it injects a button into the upper-left-hand corner of the page, as shown below:



Clicking on that button toggles the JSCoverage UI:



Once JsBloat is installed, you can also configure it to intercept requests to any URL with jQuery. For example, getfirefox.com loads jQuery from mozcom-cdn.mozilla.net, so you can set a preference in about:config to serve the instrumented version of jQuery 1.4.4 when getfirefox.com loads jQuery from its CDN. More information for JsBloat users is available on the project page.

If you are interested in extending JsBloat to perform your own experiments, the source is available on Google Code under a GPL v2 license. (I don't ordinarily make my work available under the GPL, but because JSCoverage is available under the GPL, JsBloat must also be GPL'd.) For example, if you download JSCoverage and the source code for JsBloat, you can use JSCoverage to instrument your own JavaScript files and then include them in a custom build of JsBloat. Hopefully this will help you identify opportunities to trim down the JavaScript that you send to your users.

Want to learn more about Closure? Pick up a copy of my new book, Closure: The Definitive Guide (O'Reilly), and learn how to build sophisticated web applications like Gmail and Google Maps!

Wednesday, April 6, 2011

Suggested Improvements to JSON

Today I'm publishing an essay about my suggested improvements to JSON. I really like JSON a lot, but I think that it could use a few tweaks to make it easier to use for the common man.

One question some have asked is: even if these changes to JSON were accepted, how would you transition existing systems? I would lean towards what I call the Nike approach, which is: "just do it." That is, start updating the JSON.parse() method in the browser to accept these extensions to JSON. What I am proposing is a strict superset of what's in RFC 4627 today (which you may recall claims that "[a] JSON parser MAY accept non-JSON forms or extensions"), so it would not break anyone who continued to use ES3 style JSON.

At first, you may think that sounds ridiculous -- how could we update the spec without versioning? Well, if you haven't been paying attention to the HTML5 movement, we are already doing this sort of thing all the time. Browser behavior continues to change/improve, and you just have to roll with the punches. It's not ideal, but yet the Web moves forward, and it's faster than waiting for standards bodies to agree on something.

Though if the Nike approach is too heretical, then I think that versioning is also a reasonable option. In the browser, a read-only JSON.version property could be added, though I don't imagine most developers would check it an runtime, anyway. Like most things on the web, a least-common-denominator approach would be used by those who want to be safe, which would ignore the version number. Only those who do user-agent sniffing on the server would be able to serve a slimmer JavaScript library, though that is already true for many other browser features today. I trust Browserscope and caniuse.com much more than any sort of formal specification, anyway.

Special thanks to Kushal, Dolapo, and Mihai for providing feedback on my essay.

Monday, April 4, 2011

What I learned from üjs

For April Fool's, I released a mock JavaScript library, üjs (pronounced "umlaut JS"). The library was "mock" in the "mockumentary" Spin̈al Tap sense, not the unit testing sense. It took me about a day to create the site, and I learned some interesting lessons along the way, which I thought I would share:
  • In purchasing the domain name üjs.com, I learned about Punycode. Basically, when you type üjs.com into the browser, the browser will translate it to the Punycode equivalent, which is http://xn--js-wka.com/. On one hand, this is what prevents you from buying göögle.com and creating a giant phishing scheme; on the other hand, because most users are unfamiliar with Punycode, going to a legitimate domain like üjs.com looks like a giant phishing scheme due to the rewrite.
  • I only promoted üjs through three channels: Twitter, Hacker News, and Reddit. On Twitter, I discovered that using a Punycode domain as your punchline really limits its reach because instead of tweeting about üjs.com, many tweeted about xn--js-wka.com (because that's what you can copy from the location bar after you visit the site), or (somewhat ironically) a URL-shortened version of the domain. I suspect more people would have followed the shared link if they could see the original domain name.
  • According to Google Analytics, just over half of my traffic (50.04%) on April 1 was from Hacker News (where I made it to the front page!). Another 9.89% was from Reddit. Analytics also claims that only 1.57% of my traffic came from Twitter, though 29.76% of my traffic was "direct," so I assume that was from Twitter, as well. On April 1, I had 3690 visits, and then another 443 on April 2 (presumably from the eastern hemisphere who woke up on Saturday to an aftermath of Internet April Fools' pranks).
  • GoDaddy was not able to do a domain search for üjs.com, presumably due to the non-ASCII characters. It turned out that name.com was able to, so I ended up going with them. (Presumably if I understood Punycode well enough at the outset of this project, I could have registered xn--js-wka.com through their site.)
  • I found a discount code for name.com, so my total cost out of pocket for this project was $8.75 (my hosting fees are a sunk cost).
  • Not that it was a substantial investment, but I was hopeful/curious whether I could break even in some way. I felt that traditional ads would have been a little over the top, so instead I decided to include two Amazon Associates links. Although I produced 350 clicks to Amazon (!), I failed to generate any conversions, so I did not make any money off of my ads.
  • Chartbeat is really, really cool. It made it much more fun to watch all of the web traffic to üjs.com during the day. (I wish that I generally had enough traffic to make it worth using Chartbeat all the time!) I believe that I had 144 simultaneous visitors during the peak of üjs, and I was amazed at how dispersed the traffic was from across the globe.
  • One thing that I did not realize is that Chartbeat does not do aggregate statistics. Fortunately, I set up Google Analytics in addition to Chartbeat, so I had access to both types of data.
  • Response times were about 1s on average during peak traffic times. At first, I thought that was horrendously slow, but then I realized that there were a large number of requests coming from outside the US, which increased the average. Most of the requests from the US loaded in the low hundreds of milliseconds, which made me feel good about my hosting choice (who really is excellent, btw).
  • The in Spin̈al Tap is not a valid Unicode character. Instead, it is displayed by printing an n followed by an umlaut, and what I can only assume is the kerning makes the umlaut display over the previous character. (The umlaut does not display correctly on my Chrome 10 on Windows, but it's fine on Linux.) Other characters, such as ü are valid Unicode and can be displayed with a single code point. Wikipedia has a list of letters that "come with umlauts," so I used those characters whenever possible on üjs.com, but for others, I had to use the "letter followed by an umlaut" trick.
  • var n\u0308; is a valid JavaScript variable declaration, but var \u00fc; is not.
  • Initially, the GitHub badge on my page did not link to anything, as I just wanted to satirize the trend I've been seeing in open source lately. Though after a request from a coworker, I imported all of the files I used to create üjs into GitHub. (Incidentally, when I tried to name the GitHub project üjs, they replaced the ü with a hyphen, so I renamed it umlautjs).
In the end, even though I did not make my $8.75 back, I had a really great time with this project. Although it's clear that some people on the Web don't enjoy April Fools, I think it is a nice opportunity to see some good satire. (My personal favorite was Angry Nerds by Atlassian.)

Further, satire is not completely frivolous: it is (an arguably passive-aggressive) way of making a point. In üjs, mine was this: these tiny JavaScript libraries do not help move the needle when it comes to JavaScript development from the community standpoint. Instead of contributing a tiny library, why not focus on contributing a tiny fix to a big library? Think about how many more people you will affect with a bug fix to jQuery than you will by publishing a single JavaScript file with your favorite four helper functions? Or if you are going to create a new library, make sure that you are doing something substantially different than what else is out there. For example, Closure and jQuery are based on different design principles. Both have their use cases, and they serve very different classes of development, so it makes sense for those separate projects to exist and grow.

If you have been following my blog, you probably know that I'm really big on "JavaScript programming in the large," which will be the subject of my talk at Google I/O this year. I hope to see you there!

Want to learn more about Closure? Pick up a copy of my new book, Closure: The Definitive Guide (O'Reilly), and learn how to build sophisticated web applications like Gmail and Google Maps!

Friday, April 1, 2011

Awesome New JavaScript Library!

Despite months of advocating Closure, I've finally given up and found a superior JavaScript library (and it's not jQuery!): http://üjs.com/