How We Built Betterment's Retirement Planning Tool in R and JavaScript

Engineering Betterment’s new retirement planning tool meant finding a way to translate financial simulations into a delightful Web experience.

tools and features betterment

In this post, we’ll dive into some of the engineering that took place to build RetireGuide™ and our strategy for building an accurate, responsive, and easy-to-use advice tool that implements sophisticated financial calculations.

The most significant engineering challenge in building RetireGuide was turning a complex, research-driven financial model into a personalized Web application.

If we used a research-first approach to build RetireGuide, the result could have been a planning tool that was mathematically sound but hard for our customers to use. On the other hand, only thinking of user experience might have led to a beautiful design without quantitative substance.

At Betterment, our end goal is to always combine both. Striking the right balance between these priorities and thoroughly executing both is paramount to RetireGuide’s success, and we didn’t want to miss the mark on either dimension.

Engineering Background

RetireGuide started its journey as a set of functions written in the R programming language, which Betterment’s investment analytics team uses extensively for internal research. The team uses R to rapidly prototype financial simulations and visualize the results, taking advantage of R’s built-in statistical functions and broad set of pre-built packages.

The investment analytics team combined their R functions using Shiny, a tool for building user interfaces in R, and released Betterment’s IRA calculator as a precursor to RetireGuide.

The IRA calculator runs primarily in R, computing its advice on a Shiny server. This interactive tool was a great start, but it lives in isolation, away from the holistic Betterment experience. The calculator focuses on just one part of the broader set of retirement calculations, and doesn’t have the functionality to automatically import customers’ existing information. It also doesn’t assist users in acting on the results it gives.

From an engineering standpoint, the end goal was to integrate much of the original IRA calculator’s code, plus additional calculations, into Betterment’s Web application to create RetireGuide as a consumer-facing tool.

The result would let us offer a permanent home for our retirement advice that would be “always on” for our end customers. However, to complete this integration, we needed to migrate the entire advice tool from our R codebase into the Betterment Web application ecosystem.

We considered two approaches: (1) Run the existing R code directly server-side, or (2) port our R code to JavaScript to integrate it into our Web application.

Option 1: Continue Running R Directly

Our first plan was to reuse the research code in R and let it continue to run server-side, building an API on top of the core functions. While this approach enabled us to reuse our existing R code, it also introduced lag and server performance concerns.

Unlike our original IRA calculator, RetireGuide needed to follow the core product principles of the Betterment experience: efficiency, real-time feedback, and delight. Variable server response times do not provide an optimal user experience, especially when performing personalized financial projections.

Customers looking to fine-tune their desired annual savings and retirement age in real time would have to wait for our server to respond to each scenario—those added seconds become noticeable and can impair functionality. Furthermore, because of the CPU-intensive nature behind our calculations, heavy bursts of simultaneous customers could compromise a given server’s response time. While running R server-side is a win on code-reuse, it’s a loss on scalability and user experience.

Even though code reuse presented itself as a win, the larger concerns behind user experience, server lag, and new infrastructure overhead motivated us to rethink our approach, prioritizing the user experience and minimizing engineering overhead.

Option 2: Port the R Code to JavaScript

Because our Web application already makes extensive use of JavaScript, another option was to implement our R financial models in JavaScript and run all calculations client-side, on the end user’s Web browser. Eliminating this potential server lag solved both our CPU-scaling and usability concerns.

However, reimplementing our financial models in a very different language exposed a number of engineering concerns. It eliminated the potential for any code reuse and meant it would take us longer to implement. However, in keeping with the company mission to provide smarter investing, it was clear that re-engineering our code was essential to creating a better product.

Our process was heavily test-driven, during which product engineering reimplemented many of the R tests in JavaScript, understood the R code’s intent, and ported the code while modifying for client-side performance wins.

Throughout the process, we identified several discrepancies between JavaScript and R function outputs, so we regularly reconciled the differences. This process added extra validation, testing, and optimizations, helping us to create the most accurate advice in our end product. The cost of maintaining a separate codebase is well worth the benefits to our customers and our code quality.

A Win for Customers and Engineering

Building RetireGuide—from R to JavaScript—helped reinforce the fact that no engineering principle is correct in all cases. While optimizing for code reuse is generally desirable, rewriting our financial models in JavaScript benefited the product in two noticeable ways:

  1. It increased testing and organizational understanding. Rewriting R to JavaScript enabled knowledge sharing and further code vetting across teams to ensure our calculations are 100% accurate.
  2. It made an optimal user experience possible. Being able to run our financial models within our customers’ Web browsers ensures an instant user experience and eliminates any server lag or CPU-concerns.