I'm on the Babel team and this decision was intentional. The purpose that Babel serves now is more geared towards providing the building blocks to create a JavaScript development experience rather than providing one. It's up to Ember, React, React Native, or your favorite boilerplate, cli, etc to create that nice getting started experience with healthy defaults. We either do a shitty job trying to serve everyone and everything or we take a step back and enable people more knowledgeable with their user needs to create those experiences.
Babel < 6 was trying to be everything for everyone and that wasn't working out. Everyone wanted to get their pet feature in. And we came under fire for making it easy for people to use experimental features.
That said, there is probably room to create a es2015-cli or something like it that provides a default experience, but that's orthogonal to this discussion.
Finally, the point you raise about Babel just disappearing into the stack is also misinformed. Transpiling should always be a technical decision to be made. For example, you should know that a for-of statement will transpile to include a bunch of runtime checks that would be slow in hot code etc.
I totally agree that the initial user experience for Babel could use some work. We don't tend to do a great job of guiding users through initial setup. It absolutely needs work. I'd be curious to hear the author's thoughts on what we can do better in that respect, since "no config" probably isn't going to happen off the bat.
On the other hand, I don't feel that it's reasonable to assume that setting up a build system will be trivial. Jumping into ES6 isn't and shouldn't be a trivial decision, transpilation is a complex process with edge cases. That doesn't mean it should be a pain in the ass, and I don't want it to be, but it doesn't mean it will be instantaneous and easy. Users have totally different build targets (different Node versions, different browsers, some need IE8 support, some don't). Different systems need different levels of polyfilling and differing output syntax.
Enabling everything by default is one option here, but that encourages people to not consider how things actually work, which is rarely a good idea. It also assumes that adding complexity around knowing what to _disable_ is actually better, which I'm not convinced it is.
Isn't (or wasn't, when it was 6to5?) the entire point of babel to make jumping into ES6 as easy as possible for browser users? My expectation is that the default is "Compile my ES6 to ES5 that browsers can run", and anything further than that would be configurable.
Unfortunately, because of browser support "ES5" itself is a moving target. With the release of Babel 6 they've dropping automatically polyfilling for Promises, so you'll need to include an additional polyfill or it will expect the browser supports it. Some setups use Babel to run on Node.js v4+ which supports most of ES6 natively.
There's no magical "ES6"-level of browsers which support all features of Promises, though[1]. Some products support IE11, some don't. Some only support the latest versions of Chrome and Firefox. Additionally, more people are using features not even in spec[2], known as "strawman proposals", some which are quite popular in real-life code examples[3]
Because of the moving targets on what Babel is transpiling from and to, they went for a "you decide" approach which promotes no particular level of "modern javascript" or "compiled javascript".
It's unfortunate they don't have `npm install --save-dev babel babel-preset-es2015` and "write `{"presets":"es2015"}` to a new .babelrc file" smack-bang at the top of the homepage though. The "handbook" links to a page of links, the "setup" page asks more questions than it answers. With modern Babel, no matter what build system, the above 2 steps are the same, and the 3rd is "add babel to your build system, or `npm install -g babel` and run that"
How about just shipping it with all the main presets all configured and built in?
Not even the need to understand what presets are, choose the presets, install the presets, diagnose the problems with the presets etc. Just make it work out of the box.
Making it "work out of the box" is something we had before, and we watched as proposals changed and we blew through 5 major releases in like 6 months getting more and more complicated as we went.
On top of that it was only going to get worse as people were throwing proposals around left and right and we never were quite sure what a "sane default" was. We were never sure how to keep people on track with rapidly changing proposals.
The move to a completely configured Babel, meant a move from implicit behavior to explicit behavior. People are forced to tell Babel what they want out of it.
Configuration means explicitness, explicitness means safety.
This strategy underestimates the cost and difficulty of configuration.
Hopefully the message that is coming through here is that configuring Babel is extremely hard and time consuming and error prone and is a massive, massive headache.
I am still right now trying to work out why async and await do not work after spending all day on it.
Probably for Babel experts configuration is easy and quick but for those who are not specialists in it forcing users down a configuration path is deeply, excruciatingly painful.
They need a big getting started page and direct newcommers to it where they don't try to be clever, but directly list scenarios.
Or they (or you) could make a configurator (they already have a build-tool focused one, but they need one kind of like what jQuery has) on the website, where you can select your target browser version(s), and what features you want enabled. Bam. Magic! Here's a JSON file, put it into your project root.
Async/await, I'd just go with TypeScript and then transpile the ES6 to ES5 with Babel.
>It's unfortunate they don't have `npm install --save-dev babel babel-preset-es2015` and "write `{"presets":"es2015"}` to a new .babelrc file" smack-bang at the top of the homepage though.
I think this would be a great first step towards making startup for new users less painful. Or maybe making an onboarding process similar to `npm init`, where the user is walked through a series of questions about what they need for their project and the initial .babelrc is generated automatically.
Exactly! Enabling unstable language features like decorators by default is the opposite of a novice-friendly decision, as is enabling by default features that require pulling in e.g. the Regenerator runtime.
The author of the post doesn't seem to understand that many of the transforms supported by Babel touch on proposals that are actually at various stages of being experimental or unstable, which actively should not be enabled by default for the sanity of users who don't know better.
Opting a novice user by default into a language feature that might well change is not a good thing! Otherwise we end up with something dumb like the legacy decorator transform, where people unknowing code against proposals that are liable to change under them.
>> actively should not be enabled by default for the sanity of users who don't know better
The idea that users should be protected from themselves isn't an effective one and is at the heart of the idea that Babel should do less and more should be configured in.
Nothing terrible will happen if a user uses a feature that is experimental or unstable. They might get to use it successfully however if it comes preconfigured. I can just update my code when the spec stablises.
>> the sanity of users
The sanity of users is broken by the myriad problems with Babel configuration.
It's not that the users should be protected from themselves, but that they should be protected from instability.
Proposals are constantly changing, and keeping a monolithic Babel on track with them without breaking things for users all the time is a difficult problem.
The configuration is more than just configuration, it's explicitness. It's telling Babel exactly what you want out of it. That way you never have to worry about changing behavior until you want to make the migration.
The situation is more nuanced than you are recognizing.
>> but that they should be protected from instability.
Appropriate protection is a warning in the docs "this code might change when the spec is finalised".
Inappropriate protection is hiding those features away behind difficult-to-configure barriers making it only possible for experts to configure their systems to run it.
If we don't decide to include "everything" by default, we can work on better docs (help is appreciated in where that could be improved as well as contributors!), and create a better setup experience with something like a `babel init` command. We can also run that/error when a user tries to run babel without specifying a preset/plugin.
Main gripe a lot of people I know share with me is that Babel 5 was easier, and babel-node was useful out of the box. Now it feels like getting someone's dev style getting foisted on us to get something useful out of it..
> ...but that encourages people to not consider how things actually work, which is rarely a good idea.
I'd actually take the opposite position here: people should typically not have to consider how the tools they use work, until they need to go beyond the basics.
If all I want is "the latest javascript", I should be able to get it without pondering the genius of a library's creators.
Simplification, preconfiguration, removing of the need to configure. If these were major project goals then the developers could be reminded of them every time they implement something. If a developer implements some new feature that requires (yet more) configuration then other project developers can remind them "hey was there any way you could have done this without config, or built the config in?".
I do think the kitchen sink should be included by default, even for experimental stuff. Optimisation and minimisation should be the expert case. Maybe the kitchen sink costs are size of executable or performance of compiled code but I'd gladly pay that price for something that actually instantly works without always expecting to descend into configuration hell.
Consider async/await. Why not include it fully configured out of the box? Right now I am sitting here wasting my Easter public holiday trying to get async/await to work - it still does not.
async/await are a great idea and developers will want to use them. Who cares if the standard is not yet ratified, why does that mean I should live with the cost of trying (unsuccessfully) to configure it? Why didn't the developers do it?
>> I don't feel that it's reasonable to assume that setting up a build system will be trivial.
Absolutely correct, and that's the precise reason why projects like Babel should do their utmost to make it as trivial as it possibly can be.
>> Enabling everything by default is one option here, but that encourages people to not consider how things actually work
This isn't a good enough reason to force me to do configuration.
I really want to defer the need to know how things work as far as possible into the future unless I really need to know it now. It's not that I want to be ignorant, but right now I am focusing all my learning effort on programming my application, and how the language works, and how the libraries work. Do I really need to be forced to understand how Babel works too? I'd prefer not to unless that learning directly gets me some outcome.
>> It also assumes that adding complexity around knowing what to _disable_ is actually better, which I'm not convinced it is.
Yes but better to invert the formula so that configuration is a process for experts to disable functionality in order to optimise rather than forcing beginners to enable.
Also, don't you Babel developers find you have to spend time constantly diagnosing configuration problems and errors?
> I do think the kitchen sink should be included by default, even for experimental stuff. Optimisation and minimisation should be the expert case.
A common theme in the counter-argument is that developers should be aiming to get Babel to some "ideal state" which is neither zero-config or full-config.
I'm not ES/Babel expert, but I think that's a reasonable point.
HOWEVER, I strongly feel that it is easier to get to this "ideal state" if you start with a most-uses-cases config. It's always easier to optimise from something that works, instead of trying to optimise from a broken state in the way that Babel now ships.
The author posits that "Babel should come pre configured with the kitchen sink of JavaScript development - async await, decorators etc etc."
There's a very good reason that Babel doesn't, and we should actually be fairly grateful that the Babel team have made the considered decision not to include such things.
First off, neither async/await nor decorators are actually part of ES2015. They're both TC39 proposals, at various stages of completion: https://github.com/tc39/ecma262. Very justifiably, neither Babel 5 nor Babel 6 transpiled those out-of-the-box by default; for novice programmers, using language features that are unstable proposals that have not finalized is quite dangerous – what are you going to do if those proposals change, as the decorators proposal actually has changed?
More specifically, there are meaningful issues with both of those transforms if used without further thought. Babel's stock transpilation of async/await brings in an entire runtime component in the form of the Regenerator runtime, and additionally requires that Promises exist in the JavaScript environment, which requires polyfills in the general case.
For decorators, the proposal itself is still in flux. The current version of the proposal (https://github.com/wycats/javascript-decorators/blob/master/...) is actually quite different from the old proposal, which was the one that was implemented in Babel 5. Additionally, the current proposal isn't even fully nailed down yet. Certainly one should not expect Babel to implement a language feature for which there isn't even yet a stable proposal!
This isn't to say that Babel 6 is perfect, but the specific counterexamples the author of this post brings up are extremely weak, and if anything demonstrate very good choices on the part of the maintainers of Babel.
>>what are you going to do if those proposals change, as the decorators proposal actually has changed?
I'd just update my code to meet the spec. Likely it would take vastly less time than the hours needed to fail in configuring Babel. The documentation can just say "this feature isn't finalised, it might change." That's enough. The code does not need to be disabled just because the spec isn't final.
>> quite dangerous
It's not really dangerous. No-one is going to die. It's emotive words like dangerous that lead Babel developers to think "we'd better hide that functionality behind hard-to-configure barriers so people don't cut themselves on the dangerous code".
It's extremely dangerous, we're talking about tens of thousands of developers who have chosen to depend on Babel for their livelihood, we can't just keep breaking things on them.
We don't want Babel's configuration to be difficult, we just want people to explicitly tell us what they want.
If you have suggestions on how to make the configuration easier I'm happy to hear it. But turning stuff on by default it a terrible terrible decision that hurts the community.
I love babel6 but this article made me laugh. The tragedy of babel 6 is that it used to be simple to introduce to newcomers, now I have to chain them to a desk. Now excuse me while I cry myself to sleep over how many hours I wasted configuring software.
I gave up on trying to use the babel 6 cli and wrote a global-happy replacement[1]. It still doesn't do anything by default (well, other than indent, add semi-colons, and check syntax) but typing `babel --es2015` is easier than installing a few hundred megabytes of node modules and configuring a .babelrc file just so it can convert `const` to `var`.
Indeed, I am concerned about the trend towards locally installed NPM modules, away from globally installed ones. The other project exhibiting this trend is ESLint. It used to work beautifully globally installed, then the developers broke it; it's better now, but there are still some bugs. Having one ESLint config per project and making it a direct dependency is ridiculous when you have a dozen projects and all you want is in-editor linting (in Atom, in my case).
The problem is that you can't require global modules, so that global module needs to have installed everything that needs, and if it needs to support multiple projects with different requirements, that global modules needs a whole lot of modules itself in its package.json, and now that global module is unmaintainable, because of all its dependencies. That is why global modules is generally not useful and that it is better to have them installed locally.
You could just not use Babel and ES6. It's actually pretty beginner-friendly when you go that route. It's not like you'd be missing out on features that you absolutely need to have to make an app or a backend.
Gotta agree with the author here. Trying to wade into modern JS is really really hard. There are sooooooo many different ways of doing things, and tutorials written a few months ago can be out of date.
That's not really avoiding the problem. The problem isn't just figuring out how to get all of your nice ES2015/2016 constructs to compile down to a format that can be understood by browsers and node. It's also all of the libraries that are in vogue and that you sort of need unless you're wanting to totally reinvent the wheel.
Note 'for ES6'. This is a fairly large caveat, as it requires you to have a way to deal with the emitted ES6 code. The problem being that TypeScript does not currently down-emit generators. Strictly speaking, TypeScript provides excellent down-emit support for a subset of ES6, and the rest of ES6 support such as 'let' captured in closures and generators need another compiler to down-emit the generated ES6 (for now).
There's definitely still cases where you want some subset of Babel or CoreJS polyfills AND Typescript.
The magic may be in Typescript helping you figure out which runtime polyfills to configure, maybe even spit out something resembling a .babelrc for your Typescript project.
There's definitely discussions on this topic in the GitHub Issues, though I haven't perused them recently so I can't tell you right now if there is any progress.
After a certain point, it's better for a system to be absorbed into an API instead of trying to be configurable. Then you can just write code again, and it's complex but not idiosyncratic.
A few weeks ago I wanted to modernize my JS stack after being out of the web development scene for a while, as part of learning other programming languages and the likes. I was dumbfounded at the amount of overengineering, INSANELY complex build tools, and incredible amount of tools you have to use now to simply do what JS kids call a "proper" hello world. What exactly are JavaScript developers trying to demonstrate? That they are capable of overengineering the simplest thing? (ahem leftpad?) It's getting absolutely ridiculous. And then if you complain to them about how something is kind of ridiculous, you are evil, a bad programmer, and should go burn in the pits of npm. There is also a culture for rewrite the world in JS because JS is the one and only blessed language. I'm getting inspired for a blog post now...
As an aside, I'm still stuck, having spent the whole day trying to get Babel's async/await to work. Can someone point me to a web page that includes set of instructions that actually work for configuring async/await?
This is the reason I wrote the post and I'd really like to get back to actually programming.
What I like about your solution here is that is proves installation correctness in an absolutely minimalist way.
If you made one of these for every Babel plugin then life would be a whole lot easier for people trying to work out if they have correctly configured the plugin.
Assuming of course your solutions are in a highly visible place that is obviously current and up to date.
Totally agree. When I'm spending more time debugging the build tools configuration than the JavaScript itself, there's something seriously wrong. Unfortunately, that wrong starts at npm itself and just gets worse from there.
There is a trade off being made. The whole point of the presets system is to not bring in the modules you don't need. As the Node.js runtime and browser ES6/7 feature support grows, you will not need certain plugins.
Your title is a little inflammatory: "a lesson in how NOT to design software.".
Re: Javascript tooling. Yes is it painful. But Babel is designed exactly as it should be, and the solution to fix your problem is a library that wraps around it.
He did suggest how to fix it—enable everything by default. If you'd like to argue against that then that's fine, but your response makes it look like you didn't even read the article.
I had the same experience. It was really hard to get a react/redux project started without a swathe of modules and build systems. It became a mission just trying to set it all up and Babel was a part of that. There appears to be no way around it to get that stack up and running. I discovered riot.js and moved to that as there was zero barrier to entry.
Babel < 6 was trying to be everything for everyone and that wasn't working out. Everyone wanted to get their pet feature in. And we came under fire for making it easy for people to use experimental features.
That said, there is probably room to create a es2015-cli or something like it that provides a default experience, but that's orthogonal to this discussion.
Finally, the point you raise about Babel just disappearing into the stack is also misinformed. Transpiling should always be a technical decision to be made. For example, you should know that a for-of statement will transpile to include a bunch of runtime checks that would be slow in hot code etc.