Engineering
You Don’t Have to Choose One Tool
Lessons learned moving from Ember to React
In 2013, I started exploring Ember.js as a way to build robust Javascript applications. Having done a lot of Rails work, the core philosophy of “convention over configuration” appealed to me. Ember innovated in several ways that brought real benefits to the Javascript community.
I’ve been writing Ember apps professionally for over two years. This led me to work on the Beatport streaming service, which is built with Ember—and supported by Postlight.
Ember conventions helped align the team around best practices and prevented bikeshedding over every decision. Need a build tool? Install Ember CLI and run
ember new my-app
Need a first-class router? You don’t have to think about it, it’s there. Want to deploy an app in 10 minutes? Ember CLI Deploy. Ember helps prevent gridlock over picking these dependencies, allowing you to focus on features. It gets a lot of things right.
Scaling Ember (Performance is hard)
Over time, as with any large application, we started to exercise Ember in ways that are well beyond the “getting started” guide. We fought through issues with rendering static content for search engines and social networks. We worked through the upgrade to Ember 1.13 and moving the app to Ember CLI.
As our application grew, we added more and more ambitious features. This led us to leverage the core abstraction in Ember for creating UI, the component. A component in Ember is great, and it easily composes as you create your app.
The problem, however, is that this abstraction is slow. Not mind-bendingly slow, but slow enough that it becomes noticeable to users once you start rendering a non-trivial number of them.
I added viewport-aware rendering. I swapped link-to for href-to in performance-critical components. I inlined a variety of heavily used components rendered in loops. The list expands from there. Eliminating most of the low hanging fruit, there was only so much we could do at the application level to improve the framework boot time, improve rendering, or improve Ember Data serialization time.
Our app performance is respectable on modern desktop platforms, but I wish we could hit some numbers on mobile—and we basically can’t. (We did not go so far as to reach for a custom renderer.)
Enter Code Splitting
On the hunt for further performance improvements, I started exploring an idea called code splitting. Code splitting is the idea of breaking up an application into chunks so that initial render only loads what the current page depends on. Almost all applications will grow, not shrink, over time. Code splitting prevents the natural balloon of your app bundle which will slow your app down if you are not paying attention.
The term code splitting was pioneered by Webpack, the module bundler and build tool. This was a natural place to start. I watched Pete Hunt’s talk on how Instagram works and thought, “this is exactly what I want.” When I started to really dive into Webpack, it blew me away. Webpack’s architecture is centered around explicitly imported dependencies and represents your application as a dependency graph of modules. Because of this, implementing code splitting is almost entirely done by Webpack because it can reason about each split point’s dependencies when building your actual bundles.
When I explored implementing this concept in the context of an Ember and Ember CLI application, I hit several blockers that made it near impossible. It’s true that Ember CLI gives you a fantastic getting started experience. But that experience isn’t free. The cost of this abstraction is a limitation on your flexibility to change. It’s impossible, for example, to replace Broccoli with Webpack without reimplementing most of Ember CLI. This is a daunting task that could mean giving up on Ember Addons and other useful features. That is a hard pill to swallow.
Exploring React
Around this time, the elephant in the room, React, was starting to get too big to ignore. I’d done some basic experimentation and found the API elegant, but hadn’t explored deeper than that. With the momentum of the community and projects like React Native, this was something I wanted to explore more. I started building a React application in order to see how the tools worked together.
I skipped the boilerplates (although some of them were very informative) and started with a basic project including React and Webpack. I had gotten used to Ember’s large API surface. From templates to Ember.Array, Ember includes almost everything out of the box.
While this is useful for having a self-contained framework, it’s a lot to learn and understand. In React, you have one main API at your disposal: the component. You can build your application as if you are programming for a snapshot in time, and immutability is encouraged. When implementing something outside of the scope of React, you’re encouraged to leverage the Javascript standard library or powerful design patterns. Instead of learning something framework-specific, you’re investing your time in learning APIs that are applicable to a larger domain.
Much as with Ember, the React ecosystem had most of the basics covered. But the React tools are further along. As my application grew, I added Redux for state management and React Router for routing. Redux provided a small API for predictably managing the application state. It enables amazing developer tools with features like time travel. React Router and Webpack helped me solve hard problems like code splitting and server-side rendering. I’ve been trying to solve these problems in Ember for a long time.
Maybe Ember Fastboot will deliver on the promise of simple server-rendering and rehydration. It’s also possible that Ember Engines will allow me to split my app into chunks in the future. But the reality is, if you want to ship these features today, React will take you farther.
It’s a trade-off, though. Ember is stable. React is fast-moving. Sometimes I miss that stability.
Confessing My Ember Guilt
It seems crazy that at times I felt guilty for exploring something else. I’ve developed a bond with the Ember community. I attended the first two EmberConfs, which were the best conferences I’ve ever been to. There are so many good people involved with the project. But you have to tell yourself that exploration is good. You don’t have to choose one tool. It’s better to juggle more than one framework in the Javascript world.
Learning these new tools made me realize something. It’s no secret that we can only fit so much in our head at a given time. Programming is no different. When it comes to programming languages, frameworks, and libraries, we have to be picky about what we invest our time in learning. What we learn will be what we remember, and what we remember will be what we use. Because of this, I’ve become a big believer that APIs should be as minimal as possible. If I’m going to spend time learning something, the benefits must outweigh the effort to learn and retain that knowledge.
The Javascript ecosystem is incredibly broad and moves at a rapid pace. There are new language features in ES2015. There is a movement to apply the principles of functional programming to working in JavaScript.
Ember and React approach application development from two ends of the spectrum. Ember attempts to encapsulate the functionality you might need when building a javascript application. This means that it contains more abstractions. When they get these abstractions right, it’s very powerful. When the abstraction is wrong, however, it can be hard to break free.
React, on the other hand, is just a small (but powerful) piece of the puzzle. The surrounding community has brought us Webpack, React Router, Redux, and other tools that have enabled powerful applications.
There’s room for both.
Story published on Mar 22, 2016.