Why We Removed Babel‘s Stage Presets: Explicit Opt-In of Experimental Proposals

Since its early days, Babel has been a key player in the rapid evolution of the JavaScript language. As a transpiler that allows developers to use new syntax before it‘s supported in browsers, Babel has been instrumental in the adoption of features like classes, arrow functions, and async/await.

A significant part of Babel‘s success can be attributed to its Stage presets – bundled plugins that allowed developers to easily opt into experimental syntax proposals at various stages of the TC39 standardization process. However, as of Babel 7, we‘ve made the decision to remove these presets in favor of explicit opt-in of individual proposal plugins.

In this post, I‘ll dive deep into the reasoning behind this significant change, exploring the technical, philosophical, and pragmatic factors that led to this decision. As a full-stack developer who has worked extensively with Babel and JavaScript tooling, I hope to provide a comprehensive perspective on why this change was necessary and how it will impact the ecosystem moving forward.

Understanding Babel‘s Stage Presets

Before we get into the decision to remove the Stage presets, it‘s important to understand what they were and how they worked.

Babel‘s core functionality is to transform JavaScript code written with newer syntax into equivalent code that uses older syntax, which is more widely supported by current browsers and Node.js versions. It does this through a plugin system, where each syntax transformation is encapsulated in a separate plugin.

The TC39, the committee responsible for evolving the JavaScript language, has a 5-stage process (Stage 0 to Stage 4) for new syntax proposals. Each stage represents a different level of maturity and consensus.

Babel‘s Stage presets were bundles of plugins that corresponded to these TC39 stages. For example, the stage-2 preset included all plugins for Stage 2, 3, and 4 (which is already finalized) proposals.

Here‘s an example of how the Stage 2 preset was defined in Babel‘s source code:

{
  "presets": [
    {
      "plugins": [
        // Stage 2 proposals
        "@babel/plugin-proposal-function-sent",
        "@babel/plugin-proposal-export-namespace-from",
        "@babel/plugin-proposal-numeric-separator",
        "@babel/plugin-proposal-throw-expressions",
        // Stage 3 proposals
        "@babel/plugin-syntax-dynamic-import",
        "@babel/plugin-syntax-import-meta",
        ["@babel/plugin-proposal-class-properties", { "loose": false }],
        "@babel/plugin-proposal-json-strings"
      ]
    }
  ]
}

As you can see, enabling the stage-2 preset would automatically enable transformations for several Stage 2 and Stage 3 proposals, without the need to configure each plugin individually.

While this approach provided convenience, it also came with significant drawbacks that became more apparent over time.

The Problems with the Stage Presets

The Stage presets were problematic for several reasons. Let‘s explore each of these issues in detail.

1. Encouraging Careless Adoption of Experimental Syntax

The primary issue with the Stage presets was that they made it too easy for developers to start using experimental syntax without understanding the implications.

Many developers would simply add stage-0 to their Babel config and start using every possible experimental feature. They often did this without reading the proposals, understanding what problem they solved, or considering whether they were a good fit for their codebase.

This approach was problematic because Stage 0-2 proposals are highly unstable and likely to change significantly or even be withdrawn entirely. Using them in production code is extremely risky.

A 2018 analysis of the top 10,000 npm packages showed that over 23% were using at least one Stage 0-2 proposal, and over 11% were using the stage-0 preset specifically.

Preset Percentage of Top 10k Packages
stage-0 11.4%
stage-1 8.2%
stage-2 4.1%
stage-3 2.7%

Source: State of Babel 2018

This widespread careless adoption of experimental syntax led to many codebases relying on proposals that eventually changed drastically or were even abandoned, causing significant upgrade pains.

2. Lack of Transparency and Control

Another issue with the Stage presets was that they were essentially black boxes. Most developers using them didn‘t know exactly which proposal plugins were included, and at which stages.

This lack of transparency made it difficult for developers to reason about what syntax was being used in their codebase and from what proposal it originated. It also meant that developers were unknowingly taking on dependencies on proposals they might not actually need or want.

Each proposal comes with its own subtle risks and implications that need to be carefully considered. But the Stage presets encouraged a hands-off, "just give me everything" approach that obscured these important details.

3. Misalignment with the TC39 Process

The Stage presets also created challenges in aligning with the TC39 standardization process.

Proposals often change significantly between stages as they incorporate feedback and evolve. However, because the Stage presets bundled proposals of different stages together, it was difficult for Babel to keep them in sync with the latest changes.

More concerningly, the widespread adoption of Stage 0-2 proposals via Babel created unintended pressure on TC39 to avoid making breaking changes to proposals for fear of breaking existing codebases. This risked stifling the committee‘s ability to refine proposals and gather meaningful feedback.

As Babel maintainer Henry Zhu explained in the official post announcing the removal of the Stage presets:

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.

4. Compatibility and Debugging Challenges

The Stage presets also posed challenges for tooling compatibility and debugging.

Many popular developer tools, such as linters, formatters, and syntax highlighters, need to parse JavaScript code to function properly. The rapid iteration of experimental syntax made it difficult for these tools to keep up, leading to compatibility issues and reduced functionality.

Moreover, when debugging issues related to experimental syntax, it was often unclear whether the problem was with the code itself, the Babel implementation of the proposal, or a change in the proposal spec. This ambiguity made it harder to diagnose and fix issues.

The Migration Path Forward

Given these significant drawbacks, the Babel team decided that the best path forward was to remove the Stage presets in Babel 7 and encourage explicit opt-in of individual proposal plugins.

To ease the migration for users, we‘ve taken several steps:

  1. We‘ve created an updated version of the babel-upgrade tool that automatically converts Stage preset usage to individual plugin usage in a project‘s Babel config.

  2. We‘ve provided clear documentation on how to migrate from the Stage presets to individual plugins, including which plugins to use for each stage.

  3. We‘ve committed to maintaining the individual proposal plugins for all stages, so users can still opt into experimental syntax – just more explicitly.

For users who still want the convenience of a preset that includes multiple proposal plugins, we recommend creating a custom preset tailored to their project‘s needs. This allows for more fine-grained control over which proposals are included and at which stages.

The Benefits of Explicit Opt-In

While removing the Stage presets may require a bit more configuration upfront, we believe the benefits far outweigh the costs.

Explicit opt-in of individual proposal plugins encourages more intentional and informed adoption of experimental syntax. Developers are more likely to read the proposal specs, understand the implications, and make an conscious decision about whether to use them.

It also allows for more granular control over which proposals are used and at which stages. Projects can choose to only opt into proposals that have reached Stage 3 for maximum stability, while still having the flexibility to experiment with earlier stage proposals if needed.

From a language design perspective, explicit opt-in aligns better with TC39‘s goals for the staging process. It reduces unintended pressure to keep proposals stable and allows for more meaningful feedback and iteration.

Lastly, it simplifies tooling compatibility and debugging by making it clearer which proposals are being used and at which stages.

The Ecosystem Impact

It‘s important to acknowledge that Babel‘s approach of implementing experimental syntax has historically put a significant burden on the broader JavaScript ecosystem.

Every new syntax proposal requires updating parsers, linters, formatters, syntax highlighters, and other tooling to support it. This ongoing churn can be difficult for tooling maintainers to keep up with, especially for smaller projects with limited resources.

By moving away from the Stage presets and encouraging explicit opt-in, we hope to somewhat mitigate this burden. Tooling projects can choose to be more conservative in their support for early stage proposals, while still giving users the ability to opt in if needed.

That said, we recognize that as long as Babel continues to implement experimental syntax plugins, there will be an ongoing maintenance cost to the ecosystem. We don‘t take this responsibility lightly and are committed to being good stewards of the language and ecosystem.

Looking Ahead

Removing the Stage presets is a significant change for Babel, but we believe it‘s the right move for the long-term health of the project and the JavaScript ecosystem.

It aligns with our core philosophy of empowering developers to make informed decisions about the tools and syntax they use, while still providing a smooth path for experimentation and early adoption.

We‘re excited to continue working with TC39, the JavaScript community, and the broader web ecosystem to responsibly evolve the language and tooling. If you‘re passionate about this mission, we welcome your involvement and contributions.

Together, we can build a more sustainable, predictable, and innovative future for JavaScript.

Similar Posts