Earn Rewards: Sign up now and earn a special reward after your first deposit. See offer details



Save, invest, retire

GET — On the App Store


What the CARES Act and market volatility mean for 401(k)s. See resources


Server JavaScript: A Single-Page App To…A Single-Page App

Betterment engineers recently migrated a single-page backbone app to a server-driven Rails experience.

Articles by Harris Effron
By Harris Effron Software Engineer, Betterment Published Apr. 29, 2016
Published Apr. 29, 2016
4 min read
  • Betterment recently migrated to a more simple single-page app using a UJS pattern.

  • As we add more features, we make the code even simpler. This framework also allows newer recruits to onboard faster.

Betterment engineers (l-r): Arielle Sullivan, J.P. Patrizio, Harris Effron, and Paddy Estridge

We recently changed the way we organize our major business objects.

All the new features we’re working on for customers with multiple accounts—be they Individual Retirement Accounts (IRAs), taxable investment accounts, trusts, joint accounts, or even synced outside accounts—required this change.

We were also required to rename several core concepts, and make some big changes to the way we display data to our customers.

Currently, our Web application is a JavaScript single-page app that uses a frontend MVC framework, backed by a JSON api. We use Marionette.js, a framework built on top of Backbone.js, to help us organize our JavaScript and manage page state.

It was built out over the past few years, with many different paradigms and patterns. After some time, we found ourselves with an application that had a lot of complexity and splintered code practices throughout.

The complexity partly arose from the fact that we needed to duplicate business logic from the backend and the frontend.

By only using the server as a JSON API, the frontend needed to know exactly what to do with that JSON. It needed to be able to organize the different server endpoints (and its data) into models, as well as know how to take those models and render them into views. For example, a core concept such as “an account has some money in it” needed to be separately represented in the frontend codebase, as well as the server.

This led to maintenance issues, and it made our application harder to test. The additional layer of frontend complexity made it even harder for new hires to be productive from day one.

When we first saw this project on the horizon, we realized it would end up requiring a substantial refactor of our web app. We had a few options:

  1. Rewrite the JavaScript in a way that makes it simpler and easier to use.
  2. Don’t rewrite JavaScript.

We went with option 2.

Instead of using a client side MVC framework to help enable us to write a single page app, we opted to use our Rails server to render views, and we used server generated JavaScript responses to make the app feel just as snappy for our customers. We achieved the same UX wins as a single page app with a fraction of the code.

Method to the Madness

The crux of our new pattern is this: We use Rails’ unobtrusive JavaScript (ujs) library to declare that forms and links should be submitted using AJAX. Our server then gets an AJAX rest request as usual, but instead of rendering the data as JSON, it responds to the request with a snippet of JavaScript. That JavaScript gets evaluated by the browser. The “trick” here is that JavaScript is a simple call to jQuery’s html method, and we use Rails’ built-in partial view rendering to respond with all the HTML we need.

Now, the frontend just needs to blindly listen to the server, and render the HTML as instructed.

An Example

As a simple example, let’s say we want to edit a user’s home address. Using the JavaScript single page app framework, we would need a few things.

First, we want an address model, which we map to our “/addresses” endpoint.

Next, we need a View, that represents our form for editing the address.

We need a frontend template for that view.

Then, we need a route in our frontend for navigating to this page.

And for our server, we need to add a route, a controller, a model, and a jbuilder to render that model as JSON.

A Better Way

With our new paradigm, we can skip most of this. All we need is the server. We still have our route, controller, and model, but instead of a jbuilder for returning JSON, we can port our template to embedded Ruby, and let the server do all the work. Using UJS patterns, our view can live completely on the server.

There are a few major wins here:

  1. Unifying our business logic.
    The server is responsible for knowing about (1) our data, (2) how to wrap that data into rich domain models that own our business logic, (3) how to render those models into views, and (4) how to render those views on the page. The client needs to know almost nothing.
  2. Less JavaScript.
    We aren’t getting rid of all the JavaScript in our application. Certain snappy user experience elements don’t work as well without JavaScript. Interactive elements, some delightful animations, and other frontend behaviors still need it. For these things, we are using HTML data elements to specify behaviors.

    For example, we can tag an element with a data-behavior-dropdown, and then we have some simple, well organized global JavaScript that knows how to wrap that element in some code that makes it more interactive. We are hoping that by using these patterns, we can limit our use of JavaScript to only know about how to enhance HTML, not how to automatically calculate net income when trying to distribute excess tax year contributions from an IRA (something that our frontend JavaScript used to know how to do).

  3. We can do this migration in small pieces.
    Even with this plan, migrating a highly complex web application isn’t easy. We decided to tackle it using a tab-by-tab approach. We’ve written a few useful helpers that allow us to easily plug in our new server-driven style into our existing Marionette application. By doing this piecemeal, we are hoping to bake in useful patterns early on, which we can iterate and use to make migrating the next part even simpler.

If we do this right, we will be able to swap everything to a normal Rails app with minimal effort.

Once we migrate to Rails 5, we should even be able to easily take advantage of Turbolinks 3, which is a conventionalized way to do regional AJAX updates.

This new pattern will make building out newer and even more sophisticated features easier, so we can focus on encapsulating the business logic once.

Onboarding new hires familiar with the Rails framework will be faster, and those who aren’t familiar can find great external (and internal) resources to learn it. We think that our Web app will be just as pleasant to use, and we can more quickly enhance and build new features going forward.

More from Betterment: 

Recommended Content

View All Resources

Here’s How Other Millennials Are Saving For Retirement

The retirement savings habits of the millennial and Gen Z generations might surprise you.

WebValve – The Magic You Need for HTTP Integration

Struggling with HTTP integrations locally? Use WebValve to define HTTP service fakes and toggle between real and fake services in non-production environments.

What it Means to Be a 401(k) Plan Fiduciary and How You Can Mitigate Your Risk

Fiduciary responsibilities can seem daunting and time-consuming. Learn the ins and outs of your responsibilities and which ones you can delegate.

Explore your first goal

Cash Reserve

Our high-yield account built to help you earn more on every dollar you save.

Safety Net

This is a great place to start—an emergency fund for life's unplanned hiccups. A safety net is a conservative portfolio.


Whether it's a long way off or just around the corner, we'll help you save for the retirement you deserve.

General Investing

If you want to invest and build wealth over time, then this is the goal for you. This is an excellent goal type for unknown future needs or money you plan to pass to future generations.

See details and disclosure for Betterment's articles and FAQs.