Tip - Minimizing Download Times

A frequent topic of discussion is minimizing download times for your scripts. Here are a few pointers.

Minimizing File Count

Most browsers and (separately) servers will only allow two active connections between the browser and the server at a time, queuing any others until one of those two completes. So if your HTML references a CSS file and five different JavaScript files on your server, the browser will wait until the first two requests (probably the HTML and the CSS — because it'll start downloading the CSS as soon as it sees the tag, even before finishing the HTML) before requesting the next two, then wait for those two before requesting the next two, etc.. Throw some images into the mix, and your visitors are long gone before your page renders.

So what can you do? You want to be a good engineer and keep your code modular, so… What you need is a build process, something that takes your five (or 30) distinct JavaScript files and combines them into a single download — ideally a static, versioned download to maximize cache reuse.

Your build process doesn't have to just involve your own files, either. If you're serving copies of libraries like Prototype and script.aculo.us from your own server, there's no reason not to bundle them into your "compiled" script file. Just be sure to maintain the copyright notices.

Versioning

As the result of the build process you will usually get one somewhat big file. It would be unnecessary waste of bandwidth if the user's browser needed to download it more than once. You can of course configure your web server to add an Expires HTTP header to the file. Unfortunately when the file is cached, and you updated your scripts, users can encounter problems with outdated cached script file.

To protect your users from this problem you should append version number to your file. This can be achieved in multiple ways. You could compose the name from the real versions of prototype and script.aculo.us, e.g. set it to proto-1.6.3-scripty-1.8.2-own-scripts-rev123.js to indicate that the file is composed of specific versions of prototype and scriptaculous and specific revisions of your own scripts. The other possibility is to add number of build (incremented with each build process), e.g. all-scripts-build-123.js.

If you serve a versioned file you can safely set Expires header even a hundred years ahead, just ensure your (x)html files point to current version, and your users will never use outdated scripts, nor they will download the file more than once (unless they explicitly tell the browser not to use cache).

Minifying / Packing

When writing your code, you want to be friendly to yourself and to anyone who comes after you to maintain the code. That means writing comments. That means leaving whitespace. That means using meaningful variable names. But how does that interact with your desire to have your code packed into as few characters as possible for download purposes? Again, a build process comes into play. You want to minify or pack your code, taking your "source" version and producing a "release" version.

There are several good minifiers / packers out there today. They have various capabilities, and they are variously conservative or aggressive. For instance, Crockford's jsmin is quite conservative: It removes all comments and a lot of the whitespace, but leaves your variable names alone and is very careful about removing newlines if they might be required because of the horror that is automatic semicolon insertion. On the more aggressive side are things like Packer 3 from Dean Edwards. Packer 3 does lots of things to make your code as small as possible. One of those things is variable and parameter renaming (changing your "really_useful_descriptive_name" into "_0" and such), which is great except that Prototype's inheritance mechanism uses a special parameter called $super (details here) that must have that name, specifically. If you're using a minifier like Packer 3 that modifies parameter names, you'll need to either not use that option or modify the packer's code to leave $super alone. (There used to be a project called Protosafe that used a modified version of Packer 3, but the maintainer discontinued it.)

Again, remember that most minifyiers / packers remove comments. If you minify or pack libraries, be sure that the copyright notices appear in your final version. Typically this means you'll have to put them back after minifying / packing as part of your build process.

Gzipping

All modern browsers and web servers support using gzip compression to compress content on the wire, so that your 123k JavaScript file gets zipped down to 28k (or whatever) before being transmitted, and unzipped at the browser end. This helps reduce download time tremendously.

Gzip compression must be configured on the server, whcih will then use it when the browser says it can handle it, and send uncompressed content when the browser doesn't say it can handle it. Configuration varies by web server software. There's a discussion of using gzip with Apache, and gzip is covered in the English language nginx wiki and the Russian language nginx documentation.

Google CDN

If your server is in Argentina and your visitor is in Russia, there are several hops the traffic has to go through to get from Argentina to Russia. These days there are several "Content Delivery Networks" (from AOL, from Google, etc.) that keep content in multiple places around the world and serve it from the nearest location — geographical caching. Prototype and script.aculo.us are both hosted on the Google CDN, meaning that you can have your pages load them from there rather than from your server. Not only does this reduce the load on your server, but it means that if the visitor has visited another site previously that used Prototype or script.aculo.us from the Google CDN, it'll already be in their browser cache. It also means that both connections to your server that the browser will allow are available for your other content, since the Google CDN is a different server.

To use the Google CDN in your pages, you can use simple script tags without having to have anything special from Google loaded:

<!-- Prototype -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.3/prototype.js"></script>
<!-- script.aculo.us -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/scriptaculous/1.8.1/scriptaculous.js"></script>

…or if you have the Google Ajax Libraries API loaded, you can use the google.load method. More details on the Google Ajax Libraries page. Note that either method of loading from the Google CDN does mean that Google receives a notification every time someone visits a page on your site (as the browser will send an if-modified-since request to Google).

Further Reading

The folks over at Google Code found they could markedly improve the performance of the Google Code site using several of the techniques above, particularly combining their various CSS and JavaScript files. They wrote up their findings in a blog post. Lots of good tips there.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License