Native Decorator Support (3.10)

Learn what Ember's commitment to Stage 1 Decorators means for your app code.


This feature is all about Ember's commitment to use stage 1 decorators in your Ember app code. But what does that mean?

First, let's talk about what "stage 1 decorators" means. Decorators are a new feature coming to JavaScript.

When features are added to the JavaScript language, they must first go through a process where the feature is tried, tested and improved before it can be officially adopted into the language and implemented by all the browser vendors.

This process is run by a committee called TC39, and the way features get closer to being officially adopted is by moving through "stages". All features start out at stage 1, and then as they move into stage 2 and stage 3, they become more stable, and it becomes safer for JavaScript developers and library authors to rely on them.

Because we have tools like Babel, it is actually possible to use early-stage features before they stabilize and reach later stages like stage 3. But Ember has adopted the general rule that it won't rely on any features until they are stage 3, because the risk of those features changing (and thereby requiring breaking changes to app code that depends on the previous implementation) is too high.

So that brings us to this feature, Native Decorator Support. If you look at the corresponding RFC for this feature (RFC 440), you'll see that this feature is all about Ember choosing to adopt decorators, even though decorators are still not in stage 3 of the TC39 process. That means internal Ember and Ember Data APIs, Ember app developers, and Ember Addon authors can start using decorators in their code.

So – why did Ember break its typical rule of not adopting pre-stage 3 features, and choose to adopt decorators?

The reason has to do with a prior RFC, RFC 408. This RFC explains that decorators are critical for Ember being able to move to native classes. This might seem confusing – what do decorators have to do with using ES classes? It turns out that taking advantage of many of Ember's APIs, like computed properties, actions, and service injection, is not really possible to do using just ES classes in an ergonomic way.

So, Ember can't really switch away from EmberObject and things like Route.extend() (which Ember developers are used to) to things like class extends Route (which we want to adopt, since it's now standard JavaScript) without also using decorators to bridge the gap between some of Ember's other APIs.

This is why ES classes and decorators are tied up with each, and discussed together. Because we can't really have one without the other.

So – RFC 408 was about designing and implementing some Ember APIs (like computed properties, actions and service injection) as decorators, to pave the way for ES classes. But 408 had a stipulation: decorators hadn't moved to stage 3 yet.

And that's what 440 is all about. Because decorators were basically a blocker for ES class adoption, and what eventually became Ember Octane, the framework team had to make a decision: do we wait even longer on decorators to reach stage 3? Or do we adopt them now, despite there being some risk that they could change?

A few things led to the final decision to adopt. First, the way Ember's new APIs would be using decorators was going to be very constrained. This meant that nearly all of the possible changes coming to the official decorators implementation would be unlikely to break Ember's usage of decorators.

Second, an early draft of decorators (known now as "stage 1 decorators" or "legacy decorators") made their way into Babel several years ago, and other JavaScript libraries and communities started using and relying on them heavily – notably Angular and MobX. This means that as of Ember's 3.10 release, there was effectively widespread usage of stage 1 decorators already in the wider JavaScript ecosystem. This lessened the risk even further that decorators would change in a significant way once they finally reach stage 3 and are adopted into the JavaScript language (which TC39 still expects to happen).

So – all of that to say, this feature from Ember 3.10 is really about Ember giving the green light to all the core APIs and surrounding ecosystem libraries that they can adopt decorators as per the stage 1 implementation.

It's really about paving the way for the full ecosystem-wide adoption of ES classes.

Now, one final note is that, given 3.10 shipped this feature, you might be wondering why there weren't more release notes about how to actually start using ES classes and decorators in your application code.

The reason for this is that as of 3.10, the ecosystem hadn't yet had time to implement all of Ember's existing APIs using ES classes and decorators. So, if the guides and release post were to show some ways you could start using decorators and ES classes, it would be confusing, because there would still be many other places in Ember that would require the old classes and EmberObject.extend() usage.

This is why you won't actually see ES classes and decorators in the official documentation until the 3.15 release, which is the Octane release. As of 3.15, all of Ember's core APIs were ready to be consumed exclusively using ES classes and decorators, making for a consistent and coherent experience. So, the happy path is to wait until 3.15 to start using both ES classes and decorators together for all of your Ember code.

There was a great Discourse thread on different strategies for actually upgrading an older Ember app to 3.15 and the Octane paradigm, so you might see that some teams actually do start incrementally adopting certain ES classes and decorators earlier than 3.15. That's more of a tactical decision though, and has to do more with upgrade strategy for a given team than this particular feature.

We will be making some videos discussing these strategies more in depth. But as for this feature, it's really exclusively about Ember's formal adoption and support of the usage of stage 1 decorators throughout the ecosystem.


Send us a tweet:

Or ask us in Inside EmberMap, our private Slack workspace for subscribers.