Make UglifyJS way faster by using it sooner
Grunt, Gulp, Browserify, Webpack — they all make the same rookie mistake.
- Read the many small files
- Lump them into a few big files
- Minify the big files
There are two problems. First, the minify step gets slower and slower when you add more code. Second, the minify results can’t be cached.
Adding Code Compounds Minify Wait Times
Here’s how a minifier works:
- It modifies the AST without changing logic. Basically: it renames long variables like
t. (There are other techniques that can shorten code a tiny bit more.)
- It outputs the AST as tersely as it can. For instance: it prints emptiness instead of comments; it doesn’t print extra spaces; and it writes
trueto save two bytes.
Not too complicated, right? It reads the source code and then spits it back out.
Breaking The Cache
Think of a normal development loop: edit a file; refresh the browser window; edit a file; refresh the browser window….
With the pipeline described above, yes: you have to minify the entire package every time one file changes.
And here’s where I face-palm: to speed things up, it seems every developer disables the minifier during development. Webpack’s built-in minifier is suggested for “production” mode only. Browserify’s de-facto minify plugin suggests you only use it in production.
Everybody’s wrong. You should enable the minifier during development: it means you’ll be testing the same code end-users will run. If there’s a bug in the minifier, you’ll fix it instead of deploying it.
I won’t rant any further about why splitting “development” and “production” environments is evil. (It is.) But surely we can agree that when a step is both very important and very slow, it’s better to make it fast than to nix it.
The solution is simple:
Bundlers should do this:
- Read the many small files
- Minify them, one at a time
- Lump them into a few bigger files
With this reordering, the minifier won’t face enormous files any more. It’s faster to minify one small file at a time than it is to minify a single file with all contents concatenated.
I tested with Webpack: minifying typical code early was 23% faster and made the output only 0.7% larger.
During development, this pipeline makes code edits hundreds of times faster, because Webpack won’t re-minify files it already minified.
If you’re not convinced, consider another benefit: parallel processing. The pipeline I suggest runs lots of independent minify steps: they can be run at the same time. If you have a 4-core machine, minification can be nearly four times faster than it is today. (Someday, anyway. Today’s bundlers don’t take advantage of multiple processors yet.)
Even today, there’s no good reason to disable minification in development mode.