Adopting a convention usually means eliminating inconsequential choices or, in the event that there are two or more viable options, picking one as the blessed path. Ember was launched in a much different environment than today and it made some excellent choices by:
-
Building missing language and platform features like the Ember object/class model, Ember.Enumerable and utils, RSVP, Backburner.js, and even ember addons
-
Rallying around one choice in a crowded and chaotic ecosystem, like broccoli.js, testem/qunit, JSON-API, and ember-cli.
But, as the JS ecosystem has matured, many of those initial choices are simply not needed or the community has embraced other solutions.
When jumping into an Ember app it can often feel as though you're stepping into an alternate ecosystem from the rest of the Javascript community. I'll even go so far as to say that, in 2018, any JS framework that does not support npm as a first class citizen is a non-starter. I don't only mean first class in the sense that importing npm modules "just works", but also the overall philosophy of composing large, complex software from independently useful, distributed modules.
There are good, historical reasons why Ember isn't "there yet". For one, when Ember started npm was not nearly as popular as it is now, the tooling was really rough, there were no module bundlers or talk of using CJS modules in the browser, and ES modules were far off. There is a lot of excellent work underway to get there, and Glimmer.js has floated the idea of "npm installing your way to Ember", but if Ember wants to keep pace and win over new developers it needs to fully embrace the wider ecosystem with tight integration with npm at a tooling and philosophical level.
Consider Broccoli. Broccoli has served Ember really well over the years but, as far as I can tell, it has not been embraced by the rest of the community. Ember itself is currently running on a fork of the project and it is a bit of a black box, even to experienced developers. New Ember developers have almost certainly been exposed to newer module bundlers like webpack
or Rollup and very little of their previous knowledge is transferable. That knowledge can span something as simple as importing an npm module to more complex features like code splitting and tree-shaking.
Thankfully most of the pain is abstracted by ember-cli (and better docs can definitely help), but new Ember developers are placing a much bigger bet, as they're not only opting into a view library or router or state management solution, but a suite of unfamiliar tools. React (create-react-app), Angular, vuejs, and even Rails (Webpacker) have embraced webpack. Unfortunately, Ember has been isolated by tackling the problem first and betting on tech that the rest of the community has not embraced. One of Ember's strongest assets is a commitment to shared solutions, but those shared solutions need to extend beyond the Ember community to the wider JS community. Sometimes Ember needs to lead the way, but occasionally Ember needs to aggressively adapt to where the wider community is. Getting off of in-house, legacy build tooling should be high on the priority list for Ember 4. That probably doesn't mean ditching broccoli, but making progress on the new Packager API and exposing webpack (or Rollup) config the same way ember-cli exposes eslint
or .babelrc
: sensible defaults with hooks to customize the underlying tool.
Aside from embracing tooling and ecosystem conventions, Ember could really benefit from clarifying conventions at the API and programming model level. That means "making good" on the shift to components that happened in the 2.x series. Specifically, figuring out a way to render components with model data from router transitions (routable components). Ember is a component based framework and its API's should reflect that. I would love if the mental model for getting JSON on the screen could be reduced to: URL --> Route --> Component Tree
, with the component tree having no concept of {{outlet}}
s or intervening templates– just an <Application/>
component {{yield}}
ing to child components synced with URL changes.
Another way to solidify the programming model would be to deprecate two way bindings and fully support DDAU. That could mean making computed properties read-only by default or explicit support for immutable objects or more guidance about where to handle actions that update data– maybe all of the above! But, my impression is that one way data flow still isn't conventional (or the natural default) and some API help is needed.
API's and ecosystem/interop can be improved by embracing "just Javascript" wherever possible. To me, that means:
-
ES6 classes and decorators
-
Full
async/await
support at the framework level, i.e. no runloop interop problems in tests and native Promise support (bye bye RSVP) -
No prototype extensions. Move all Enumerable methods and other utils (isPresent(), etc) to addons.
-
This may be controversial, but maybe even removing Ember's custom DI/injection system with plain ES module
import/export
:export default Component.extend({ flash: service() });
with:
import flash from 'app/services/flash'; export default Component.extend({ flash });
Details aside, the general idea is to make Ember feel like "native" Javascript as much as possible, building on knowledge developers already have and making the places where it diverges, divergent for good reason.
The exciting thing is there's already incredible progress on everything mentioned above. But, these are suggestions for the 2018 Roadmap and roadmaps are about prioritization and focus. If Ember were to fully embrace the current Javascript ecosystem it would bring tremendous benefits for happy, longtime users and also entice potential new users to give it another, well deserved look.
@rtablada thanks for commenting. I'm curious to hear what pain you've encountered from
import
-ing singleton objects. I've never ran into problems while working on react stuff. That said, I'm excited to see howimport/export
can be used more and also how the DI concept can be refined for use cases where runtime lookup or the container is really necessary.