Saturday, March 30, 2024

The two original sins of Ember.js

Frameworks for building web applications in a browser have come and gone a lot. One that got a fair bit of attention for a while but is much more obscure now is Ember.js. Was this for no particular reason or did Ember.js have a fundamental flaw?

There is a lot to like about Ember: two of my favorites were extensive documentation and careful thought to versioning and upgrade paths.

But I maintain it had two fundamental design mistakes and that although there has been some efforts in recent versions to roll back these original sins, that changing something this fundamental, while possible, is not a small patch.

The first is that Ember wanted to own everything rather than live in a larger javascript ecosystem. This is most concretely illustrated by how many things used to hang off the global Ember object, including functionality where there are other javascript packages, perhaps popular and well functioning ones, for the same purposes. As far as I can tell Ember has been largely successful in rolling back this tendency, partly driven by developments such as improved tooling for managing packages and improved functionality in browsers themselves.

The second is that Ajax HTTP calls in Ember do not happen in a controlled way. This is most clearly illustrated by the way that Ember Data associations return a promise. While at first glance this is just similar to lazy loading in server ORMs (itself a more controversial concept than when Ember began), in the browser context this kind of laziness is worse, because a server call from a browser is a more user-visible operation and the implication for things like debuggability are worse.

There is a solution to this in  front-end frameworks, known as "actions up, data down". The application code (rather than the framework acting behind its back) makes an Ajax call, with a handler that when the call completes will generate an action. An action is sent up a component tree to a place where it can then be sent down to those parts of the system which need to update what is shown on the screen. For example, turning spinners into real content, or whatever exact choice your application makes about what is displayed while data is loading and what is displayed once it has loaded. While recent versions of Ember do give lip service to actions up, data down, really coming to terms with what it means for Ember Data is a bigger change than just bolting on two conceptually different mechanisms into the same framework.

There are a lot of reasons that web frameworks come and go and it is possible that my own difficulties with Ember applications had little to do with these two issues. And as I say, Ember did make efforts to repent for both of these decisions (to stretch the sin metaphor farther than I probably should). But I can't help but wonder whether the effort to revamp designs as fundamental as these was just too heavy of a lift, at a time when Ember was trying to remain in people's minds and attract contributors to make the changes they had laid out.