Ember Data Origins: Drafting on Rails (Part 1)

Introduction

I've spent over a decade working with Ember.js across more than a dozen production applications. I came to Ember from Rails, and the fit felt natural. This wasn't coincidental. Ember.js had deep roots in the Rails community. Yehuda Katz, one of Ember's two co-creators, was also a significant Rails core contributor. The philosophies aligned: convention over configuration, developer happiness, and opinionated tooling that let you focus on building rather than bikeshedding.

During that time, I've watched Ember.js evolve into a beacon of stability, a framework that proved you could modernize without abandoning your users, that provided clear upgrade paths across major versions, and that treated backwards compatibility as a feature rather than a burden. And then there was Ember Data, which repeatedly upended the contracts it established with its ecosystem.

What made Ember Data's trajectory so jarring was the contrast with Ember.js itself. The framework has a disciplined release process built on predictability and respect for existing users. New features arrive in minor releases without breaking existing applications. Deprecations follow a methodical path: introduce the new approach, provide a migration path, deprecate the old, then remove it in a subsequent major release. Major versions are, by design, "not exciting, just a predictable point where some cleanup happens." The LTS program provides 36 weeks of bugfixes and 54 weeks of security updates, giving teams confidence they can upgrade on their own schedule.

Ember Data would come to throw much of this out the window. But it didn't start that way.


The Golden Era: Ember Data 1.x and Rails

Ember Data 1.x represented a genuinely compelling vision. Out of the box, it integrated beautifully with Rails using the default serialization. The conventions aligned: you set up your Rails API following standard patterns, and Ember Data would just work. Relationships, nested resources, async loading, all handled with minimal configuration.

For Rails shops, this was transformative. You could build ambitious client applications without reinventing data management. The adapter and serializer patterns were clean abstractions that rewarded understanding the conventions. It wasn't perfect, but it was coherent.

This tight coupling to Rails conventions would later be characterized as a limitation, but for the teams actually using it, it was the entire point. We weren't building abstract data layers; we were building Rails applications with rich JavaScript clients.


The JSON:API Pivot

Ember Data 2.x introduced JSON:API support as the new default, presented as an evolution toward standards-based development. The community was told that adopting a specification would provide long-term stability and interoperability.

To its credit, ED2 handled this transition the right way: JSON:API became the default, but the existing REST serialization continued to work. Teams could migrate on their own schedule. This was the Ember upgrade philosophy in action: introduce the new approach alongside the old, give people time to adapt.

And JSON:API itself brought real improvements. The specification provided a consistent structure for representing relationships, pagination, and error responses. It standardized patterns that every team had been solving ad-hoc. I genuinely like JSON:API, and once the migration was complete, the structured approach paid dividends.

But Ember Data's JSON:API implementation was incomplete. The specification supported nested data (a common pattern in real-world APIs), but Ember Data did not. This wasn't a minor omission. For applications with deeply nested data structures, it was a fundamental limitation that required significant workarounds.


The Community Responds

When Ember Data's public API couldn't solve a problem, the community built solutions. ember-data-model-fragments handled nested, non-relational data structures that JSON:API's flat approach couldn't accommodate. ember-m3 explored schema-driven models. ember-data-storefront offered alternative data access patterns. ember-changeset provided streamlined mutation flows.

These addons had to use private, undocumented APIs because Ember Data's public API was insufficient. In a 2023 blog post, the Ember Data team acknowledged this reality: they "appreciate each of these addons for tackling challenging aspects of working with EmberData and addressing the genuine needs of our consumers," recognizing that "in the absence of the public APIs that EmberData now offers, many of these libraries built features on an unstable foundation."

The team promised a path forward. New public APIs (RequestManager and Cache) would let these libraries "rewrite to utilize public APIs and achieve improved stability." They intended "to bring most of the ideas explored by each of these libraries into the core recommended experience."

Note the key word: rewrite. The new APIs weren't something addon maintainers could incrementally adopt. They required fundamentally rearchitecting both the addons and the applications using them.

That was the promise. Part 2 covers what actually happened.

At this point, things were still workable. The JSON:API migration was painful but paid off. Model Fragments filled a real gap. Teams were shipping software. The ecosystem had found a rhythm.

What came next would change that.


Continue to Part 2: Going Off The Rails