Resources

Raising the Limits on Developer Speed

How we used React/Redux, Jest, and automation to rebuild our dashboard in only three months

Up until five months ago, our dashboard was slow, limited, and (worst of all) written in Angular. We don’t have anything in particular against Angular itself, but nobody on our team had any significant experience with it. This meant that fixing even a tiny bug took a large amount of effort. We had big plans to add awesome features to our dashboard — organizations, multiple applications per user, queryable logs, and more! — but we weren’t convinced that building on top of our legacy code was the best use of our time. So we scrapped it.

Our goal was to build the new dashboard in a way that would enable us to develop quickly and effectively. We knew we wanted to use React, to keep in line with our other in-house JavaScript projects, but the dashboard has a considerable amount of application state, so we decided to use Redux as well. We started by taking a bunch of pointers from Bumpers’ Isn’t our code just the *BEST* 🙄 article.

Instead of having all of our store files directly at the /store level, as is common in many Redux applications, we split the store into several subdirectories, each one with their own actions, reducers, and selectors files. Each of these subdirectories correspond to one of our models, e.g. Account, Organization, Application, etc. This made development much faster by grouping relevant code together. Now, when someone wants to make changes to the Account state, they only have to navigate to /store/account to find that code, rather than having to ctrl+f in massive files that bunch all of the code for different models together.

Sidenote: Another point we took from the Bumpers article was the addition of an endpoints file in each of these store subdirectories. The API calls can be handled in this file and called in-turn by the actions file. This is a nice separation-of-concerns that makes it easy to switch out either the actions or the API interface without affecting the other, and provides a clean stub interface for testing.

This separation made it extremely easy to modify stores, but unfortunately, adding new stores required a lot of prep work. For each new store, we had to create a new directory, add a bunch of files, and write the same initial skeleton code — all work that could be automated! We wrote a scaffolding script to do it for us and added it to our package.json file. We were able to run this script with Yarn via yarn scaffold <type> <name>. We added several other pieces of skeleton code to this script, so we could create quickly components and model definitions as well as stores. This made the process of adding new code super easy and fast. Most of the code only needed a few details to be fleshed out and then it was good to go. 👍

Another thing that we did to increase our development speed was to use Jest for testing. We believe testing is an important part of development, but writing tests can significantly slow down the development process. Jest’s visual output is fantastic (on par with pytest for Python), but the real kicker is its concept of snapshots.

Using Jest’s snapshots is as simple as expect(testData).toMatchSnapshot(). The first time this test is run, the user will be prompted to inspect testData and confirm that it has the expected value. Jest then creates a snapshot file with the confirmed data value. On each subsequent test run, Jest will compare the current value of testData to the value stored in the snapshot. If the values do not match, the console will show the differences between the two values and ask if the snapshot should be updated.

This is much faster than having to 1) figure out what the value of data should be when initially writing the test (particularly if it has a complex structure), and 2) having to go change a bunch of hard-coded test values when something does legitimately change (which happens a lot in early development stages). We were able to add skeleton tests to our scaffold script, so each React component automatically got a test like this:

This helped ensure that all of our components were tested. If it was a simple component, the skeleton test was all it needed. If it was a more complex component, the test would fail and prompt the developer to update the test to be more accurate. This effectively eliminated the case where developers forget to write a test for a component, and not having to do any extra work to test the simpler components was a huge plus.

Sidenote: Prettier, an auto-formatter that we talked about in our tooling blog post, also did wonders for our development speed. You don’t realize how much time you spend on spacing until you no longer have to do it.

As these changes were integrated with our workflow, we were able to develop faster and faster. We completed the entire dashboard project (along with the associated backend changes necessary to support the new features we wanted to implement) in an astounding 12 weeks. The key lesson we learned from this experience was that investing the initial time into building a strong developing foundation is well worth the effort! For this project, building that strong foundation involved grouping code together in a functional way and automating as many patterns as we could. Going forward, we’ll be sure to look out for ways that we can do the same in our other projects!

We had a rising trend in the number of commits to the dashboard project, until it neared completion.This was with a team of 4-5 engineers, depending on the week.

About Author

Halla Moore

Halla grew up in Florida and studied CS at MIT. She loves drawing and sunshine, but somehow found her way back to Boston after 16 months in SF.