by Henry Zhu
Why We Removed Babel’s Stage Presets: Explicit Opt-In of Experimental Proposals
Moving forward with v7, we’ve decided it’s best to stop publishing the Stage presets in Babel (for example,
We didn’t make this decision lightly, and wanted to show the context behind the interplay between TC39, Babel, and the community.
A Babel preset is a shareable list of plugins.
Each preset (ex.
stage-2, and so on) included all the plugins for that particular stage and the ones above it. For example,
stage-3, and so on.
This allowed users who wanted to use experimental syntax to simply add the preset, instead of needing to configure/install each individual plugin.
We actually added the Stage presets shortly after Babel’s v6 release (it was previously a config flag in v5). Shown below is an older example.
Looking back, it worked really well! (Maybe too well?)
Too Good a Job?
We probably wouldn’t be where we are if not for the wide adoption of compilers such as Babel: it accelerated the usage (and teaching) of ES2015 to a much larger audience. The growth of React further fueled usage, as its JSX syntax, class properties, and object rest/spread led to people knowing a bit more about these syntax proposals.
Babel became a one-time setup for people, never to be thought of again. It became the underlying infrastructure, hidden behind other tools until there was a
SyntaxError, dependency issues, or integration issues. Simply use
This was awesome to see in some ways, as it meant that these ideas were being tested in the wild, even in production environments. However, it also meant that many companies, tools, and people would encounter some trouble if a proposal changed in a significant way (or even got dropped altogether).
Back and Forth
However, we found that keeping the Stage presets would lead to issues even for Babel itself:
- It was a common issue to ask something like: “What presets(s) are needed to use async functions?” It would be unclear for people to know exactly what
stage-0meant, and few people would look at its
- Removing a proposal plugin in Stage 3 (once it moves to Stage 4) is actually a breaking change. This issue is exacerbated when you are trying to use
@babel/preset-envto not compile a natively supported proposal.
Part of the issue is precisely around naming things, and as we often hear, naming things is hard.
There were a lot of names for ES6 itself: Harmony, ES Next, ES6, ES2015. When people hear about new ideas, it makes sense to just pick the latest number and attach the name to it.
The reasoning is that saying “ES7 Decorators” assumes that Decorators is expected to be in ES7. I mentioned this in my last post regarding compiling node_modules, but being in a particular Stage doesn’t guarantee much: a proposal can stall, move backward, or get removed entirely.
We wanted to highlight this fact when we decided to change the names of the proposal plugins from
Having presets for proposals so early in the process may imply that these proposals are guaranteed to move forward or have a stable implementation.
TC39 urges caution when using Stage 2-or below proposals, as it might result in inadvertent pressure from the community to keep the implementation as-is instead of improving it for fear of breaking existing code or ecosystem fragmentation (for example, using a different symbol like
# instead of
@ for decorators).
After compilers like Babel made it common practice for people to write ES2015, it was natural for developers to want to try out even newer and more experimental “features”. The way this worked in Babel was to use the
stage flag in previous versions or the
Being the most convenient way of opting into any new feature, it quickly became the default for people when configuring Babel (even though in Babel v6 it moved to not doing anything by default, which caused lots of complaints).
There were a lot of good discussions even years ago, but it wasn’t the easiest thing to navigate: we wouldn’t want to penalize users who understood the tradeoffs by putting
console.warns when using it, and not having the option at all seemed unreasonable at the time.
Blindly opting into Stage 0 (whether we had it by default) or people choosing to do so seems dangerous, but also never using any proposals is overly cautious. Ideally, everyone should able to make an informed decision about the kinds of features that seem reasonable to them and use them wisely, regardless of what stage they are in. Mike Pennisi wrote a great post about these concerns.
We also could have tried to:
- Rename the presets to better signify the stability level (doesn’t solve the versioning problem)
- Better versioning strategies: independently version the presets and update them immediately when needed, maybe use
- Warn/Error for old out-of-date versions of presets
In the end, people would still have to look up what proposals are at what Stage to know which ones to use if we kept the Stages in.
Why not remove it earlier? The Stage presets have been part of Babel for years, and there were concerns with adding more “complexity” to using Babel. A lot of tooling, documentation, articles, and knowledge were built around the Stage presets. Earlier, we thought it was better to officially maintain the presets since someone else would (and will) inevitably create them.
We’re trying to determine the right level of feedback: if it’s only the committee that decides what goes into the language, it may lead to well-specified features that are not needed. But if the community expects that in-progress, experimental proposals are considered stable or ok to use in production without consequence, then we’ll have other issues. We all want to move forward and proceed with intention: not rushing, but not being too cautious. Babel is the right place to do that experimentation, but knowing where the boundaries are is necessary.
Removing the presets would be considered a “feature,” since it means someone would have to make an explicit decision to use each proposal. This is reasonable for any proposal, since they all have varying levels of instability, usefulness, and complexity.
We fully expect some initial backlash from this, but ultimately feel that removing the Stage presets is a better decision for us all in the long run. However, removing previous defaults or removing the Stage presets doesn’t mean we don’t care about ease of use, new users, or documentation. We work with what we can to keep the project stable, provide tooling to make things better, and document what we know.
For a more automatic migration, we have updated babel-upgrade to do this for you (you can run
The TL;DR is that we’re removing the Stage presets. At some level, people will have to opt-in and know what kinds of proposals are being used instead of assuming what people should use, especially given the unstable nature of some of these proposals. If you use another preset or a toolchain, (e.g. create-react-app) it’s possible that this change doesn’t affect you directly.
We have deprecated the Stage presets as of
7.0.0-beta.52. If you don't want to change your config now, we would suggest you pin the versions to
beta.54 until you can upgrade. After
beta.54 we will throw an error with a message saying how to migrate. And check that all your versions are the same while in prerelease.
As an alternative, you are free to make your own preset that contains the same plugins and upgrade them as you please. In the future, we may want to work on a
babel-init that can help you set up plugins interactively or update
babel-upgrade itself to list and add the current Stage plugins. Maybe Babel should stay a low-level tool and rely on other higher-level/framework tools like
create-react-app to handle these choices for people.
Preventing Proposal Lock-In
The main point in the post is that the proposal itself is in flux and has a few options to explore. Because we’d like to implement all three of the current possibilities as Babel plugins for both spec feedback and user feedback, we believed the way the plugin is used should change as well. This is a relatively new approach for TC39 and Babel!
Previously, we would add the proposal plugin to the config and that was it. Now, we remove the default behavior and ask users to opt into a flag that shows which proposal is chosen. We make it clear that there isn’t a fixed (or even favored) option at the moment.
This is something that we’d like to continue to do moving forward as another indication that these proposals are open to change and feedback from all of us. The removal of the Stage presets makes this even easier, as before we had to pass down these options even if you didn’t use the syntax.
Ecosystem Maintenance Burden
Once new syntax is proposed, many things need updating: parsers (
babylon), syntax highlighting (
language-babel), linters (
babel-eslint), test frameworks (
ava), formatters (
prettier), code coverage (
istanbul), minifiers (
babel-minify), and more.
There have been many issues brought up on projects like
typescript, and others to support Stage 0 proposals because they were in Babel. There aren't many projects that would adhere to a policy that required them to support any proposal, since that would be extremely demanding to maintain. In many ways, it's surprising we even attempt to handle it in Babel itself given the constant updates and churn.
Who is doing that work, and is it our responsibility to make sure everything works? Every one of those projects (mostly volunteers) is lacking in help in almost every aspect, and yet we continually get complaints about this across the board. How are we, as a community, to take responsibility for dealing with our infrastructure (not dissimilar to open source as a whole)?
Babel has taken on the unusual burden of supporting these experimental features. At the same time, it’s reasonable that other projects take a more conservative policy. If you’d like to see new language features are supported across the ecosystem, contribute to TC39 and this project to bring these proposals to Stage 4.
If you appreciate this post and the work we’re doing on Babel, you can support me on Patreon, ask your company to sponsor us on Open Collective, or better yet get your company involved with Babel as part of your work. We’d appreciate the collective ownership.
Originally published at https://babeljs.io/blog/2018/07/27/removing-babels-stage-presets.