Client-side code must remain client-side

Greetings, earthlings! As far as I'm concerned about the ongoing pouring tons of bullshit on JavaScript developer community (and by whom? Who are the "judges"?) and fight this bullshit on all possible levels, I definitely can't understand some trends you can spot here and now. And the field where I, a usual JS developer, encountered these trends, worries me much more than excites.

ES6 (yes, I know about the official "ES2015" name but that year-based naming always annoyed me) standard is a huge move forward for the entire JS. And while CoffeeScript is a damn great language, its compiler still doesn't allow to select the target platform: ES3, ES5 or ES6. So what's the point of, for example, generators in CS if CS classes still don't compile into native classes? If CoffeeScript compiler developers don't fix this issue, I think that the language's role will diminish as native ES6 implementations will spread.

Well, the rest of my post is devoted to the lack of native ES6 implementations in the browser world right now (March 2016): we still have to support some unterbrowsers like IE (not Edge) and iOS Safari. And seeing the tremendous movement of ditching CoffeeScript in favour of ES6, I truly wondered whether this was really a worthwile movement and how on Earth I could actually start writing ES6 for ES5-enabled browsers. And here he comes: Isaac Babel.

Don't get me wrong, Babel is a really nice tool that does its best to do what it claims: compile ES6 code into ES5. Surely, some features will remain unsupported forever because they require native engine support (such as proxies), but Babel really does what it can to bring as many features into ES5 world as it can. So we have absolutely no reason to bash this wonderful tool. The problem is... Babel is often not enough. Sure, it would be enough if we were writing ES6 code for NodeJS. We can use Babel (along with some helper modules) and nothing more. But the task is to support unterbrowsers, remember? And for client-side support the story goes far different way.

I'll not go into much detail of Babel's inner working, but one must understand that in general there are two types of Babel plugins: transform plugins and runtime plugins. The latter require you to import them right into your code. I felt that was a bad idea from the very beginning. Unfortunately, that was no mistake. The single most important runtime plugin which plenty of ES6 features depend on is called babel-polyfill. It's based upon Regenerator and CoreJS and provides support for generators, new array methods, promises, symbols etc. What does it mean to us? It means that if we want to just use new ES6 syntax elements (fat arrows, spread operator, default arguments etc... even classes), we don't need this plugin, but if we want to use any significant ES6 feature, we must import it.

In the first case, the story ends, you just use Babel without this polyfill and your code is ready to use in any ES5-compliant browser. But what about the second case, where you want a complete ES6 environment? Well, this is where the real "fun" begins. Babel translates import statements into just require() calls. We want to write client-side ES6 code, remember? Not a single import / require() was needed before this. And now we have to roll another compilation layer to translate this require('babel-polyfill') call into something that a browser can understand, with no additional actions from our side.

Enter Browserify. It automagically assembles your module dependency tree into a single browser-ready file. And it seems to be the only correct way to get this madness working in the browser. It adds some madness on its own, but that's easily circumvented with --bare switch. Throw in some UglifyJS after proper Browserify call, and you finally will look at your cross-browser shiny script... that weighs over 109 KB.

Wait, what?! 109 KB (minified) over my own script?! An example compiled with Elm weighs less than that!

Yes, that's the real price for upgrading legacy environment. The babel-polyfill plugin itself seems to depend on lots of stuff that brings its own weight to the bundle. 109 KB is nothing for a server environment like Node, but is a significant overhead for a browser. Especially on mobiles that generally have less computing power than PCs and much slower Internet connection. Heck, lots of people ditch jQuery for its size, and now we have to drop in an even bigger file just to use a new language standard! Isn't it insane?

All that brings me to the single point:

Always target client-side platform when you write client-side JS!

Stop trying to be fancy. Stop bringing server-side conventions into browser-oriented codebase. Stop, in all means, writing browser JS in a way that doesn't match your real target platforms. If you don't plan to support non-ES6 clients, write ES6, if you do, write ES5 or CoffeeScript (which you were so desperately trying to ditch). Leave babels-shmabels to the realm they belong to, which is NodeJS. Go native or go home.

Discuss on Reddit, screw Hacker News!

P.S. Update: thanks to some good people from Reddit who proposed Rollup instead of Browserify, I managed to reduce the same code from 109 to the whopping... 90 KB. Nice try but... the problem is still inevitable.