Managing CSS And Javascript In Production Is Hard
You develop your site, and you are going to deploy it to production. Assuming you have a lot of traffic, you’d need to optimize your assets (javascript and css). Some of these things are also valid for images, but there’s more to be said there, so I’ll focus on css and javascript. It’s way more complicated than you’d like. Here are the steps:
- merge – all your javascript and css files should be merged into a single file so that the browser fires only one request. Frameworks sometimes have tags to do that, otherwise you have to merge them in your build and then use the merged version.
- minify – before or after getting the files merged you need minify them. That is, get rid of all useless symbols, which make the files readable, but increase the file sizes. This normally happens when you build your application, though minification-on-the-fly-and-then-cache is also a good option
- version/hash – normally your assets should be cached by browsers for a long time, so when you make a change you need a good way to make the browsers refresh them. That’s why you append a version (/styles/1/main.css) which you can configure in your app, or you compute a hash of the resource and use that as automatic version. (Of course, always set a long expiry header to assets, so that they are requested only once by each user)
- gzip – after the files are merged and minified you should gzip them before sending them to the browser. This is handled by most servers and/or frameworks, so it sounds like an easy bit. But read on for some complications
- CDN / asset server / server cache – that’s the hard part that messes things. You don’t want your assets to be loaded from disk, merged, minified and gzipped each time they are served, because this takes resources, and assets don’t change that often anyway. That’s why you put them in a CDN or use a custom asset server with some cache like Varnish in front. Ideally, you should be able to point the CDN to your server which does all the above dynamically. But often you should pre-generate the versioned, gzipped, merged and minified version of the asset and deploy it somewhere.
There’s another complication: development mode. Your application should support non-cached, dynamic going through the above steps. There exist many utilities to help you with that. RoR has asset pipeline, Java has jawr (which I don’t recommend, btw), others have their options as well. But there is one important thing that you should try to follow: always prefer dynamically generating the final form of the assets (by the site/framework), and avoid performing any of the above operations in the build process. If you put it in the build, it automatically becomes a lot more tedious – you need to ship two versions (gzipped and non-gzipped), merge the resources outside a web context (the build is web-agnostic), append the asset version during the build (if you don’t use the automatic versioning based on the hash). Then the packaged assets need to be somehow distributed to the CDN or you your asset server. And if you make it all dynamic, all you need is your framework to support an “asset url” property that is used in the webpage. How that works:
- when your page is generated, the asset url is used to point to the asset server / CDN.
- your application is still able to serve the assets dynamically, so that the CDN/asset server can pick them up
This means you should package the assets in your application, rather than outside of it. This is debatable, and some people prefer to package them separately and distribute them, but with all the steps given above, it becomes harder to manage. The last point here is how you deploy changes to the assets – is it together with the whole application, or just the css and js files? If assets are packaged separately, you can deploy them separately. Otherwise you’d have to deploy the whole app (or manually copy files). Choose which one fits you better. But have in mind that it’s not easy and you should put some thought into it early in the development process.
You develop your site, and you are going to deploy it to production. Assuming you have a lot of traffic, you’d need to optimize your assets (javascript and css). Some of these things are also valid for images, but there’s more to be said there, so I’ll focus on css and javascript. It’s way more complicated than you’d like. Here are the steps:
- merge – all your javascript and css files should be merged into a single file so that the browser fires only one request. Frameworks sometimes have tags to do that, otherwise you have to merge them in your build and then use the merged version.
- minify – before or after getting the files merged you need minify them. That is, get rid of all useless symbols, which make the files readable, but increase the file sizes. This normally happens when you build your application, though minification-on-the-fly-and-then-cache is also a good option
- version/hash – normally your assets should be cached by browsers for a long time, so when you make a change you need a good way to make the browsers refresh them. That’s why you append a version (/styles/1/main.css) which you can configure in your app, or you compute a hash of the resource and use that as automatic version. (Of course, always set a long expiry header to assets, so that they are requested only once by each user)
- gzip – after the files are merged and minified you should gzip them before sending them to the browser. This is handled by most servers and/or frameworks, so it sounds like an easy bit. But read on for some complications
- CDN / asset server / server cache – that’s the hard part that messes things. You don’t want your assets to be loaded from disk, merged, minified and gzipped each time they are served, because this takes resources, and assets don’t change that often anyway. That’s why you put them in a CDN or use a custom asset server with some cache like Varnish in front. Ideally, you should be able to point the CDN to your server which does all the above dynamically. But often you should pre-generate the versioned, gzipped, merged and minified version of the asset and deploy it somewhere.
There’s another complication: development mode. Your application should support non-cached, dynamic going through the above steps. There exist many utilities to help you with that. RoR has asset pipeline, Java has jawr (which I don’t recommend, btw), others have their options as well. But there is one important thing that you should try to follow: always prefer dynamically generating the final form of the assets (by the site/framework), and avoid performing any of the above operations in the build process. If you put it in the build, it automatically becomes a lot more tedious – you need to ship two versions (gzipped and non-gzipped), merge the resources outside a web context (the build is web-agnostic), append the asset version during the build (if you don’t use the automatic versioning based on the hash). Then the packaged assets need to be somehow distributed to the CDN or you your asset server. And if you make it all dynamic, all you need is your framework to support an “asset url” property that is used in the webpage. How that works:
- when your page is generated, the asset url is used to point to the asset server / CDN.
- your application is still able to serve the assets dynamically, so that the CDN/asset server can pick them up
This means you should package the assets in your application, rather than outside of it. This is debatable, and some people prefer to package them separately and distribute them, but with all the steps given above, it becomes harder to manage. The last point here is how you deploy changes to the assets – is it together with the whole application, or just the css and js files? If assets are packaged separately, you can deploy them separately. Otherwise you’d have to deploy the whole app (or manually copy files). Choose which one fits you better. But have in mind that it’s not easy and you should put some thought into it early in the development process.
2 thoughts on “Managing CSS And Javascript In Production Is Hard”