case_study/rhino: breaking down a mega-form into manageable chunks
At Rhino, we have internal tooling that allows property managers to complete many different tasks for our enterprise partners. Rhino has invested well in making sure this internal tool is built out and designed as well as many of our public-facing tools.
Recently, the responsibility for a specific set of tooling was transferred to me — and my pod and I made a number of improvements to make the tool more user-friendly, accessible, scalable (through the use of design system reusable components), and efficient.
A note about our UI
Our product involves a UI that consists of a sidenav, a main body (usually a list of cards), and a detail panel on the right (that is opened when cards are clicked). Because of certain technical constraints, it was most effective if I kept my work isolated to the detail panel.
Improved UX – no more giant forms
A lot of the existing designs involve forms. We need to be able to get information from the users in order to accomplish our tasks. However, it felt as if every new iteration involved appending another section to one large form.
Although this was a great way to quickly solve the immediate problem, it became unwieldy over time — and eventually started to impact performance, as multiple calls had to be bundled together by engineering.
Our pod’s first goal was to figure out how we could break this form into smaller chunks with two main goals:
- Can we only ask the user for the information necessary to complete a single task at a time?
- Can we increase performance by separating those bundled calls?
Our first mistake
Before we had come to the conclusion that we should break down the mega-form, we spent some time designing a progress bar to keep the user informed after they had submitted the form. It could often take 10+ minutes for everything to be complete, so we thought it was important to communicate progress to avoid resubmits.
Additionally, by breaking down the steps into an approachable UI, we could also expose contextual errors — and give directions as to how to resolve those errors.
However, once we decided to break up the mega-form itself, this progress bar wasn’t needed anymore. Instead, we were able to separate the calls and expose the error states directly on our new UI.
Breaking down the mega-form
After spending some time with developers and our PM trying to wrap my head around what’s happening in the mega-form, we realized we could break it down into three chunks.
- The initial “Integration setup” which allows the user to run a “Resident data pull.”
- “Coverage rules” which allows the user to send auto-invites.
- “Enable policy push” which allows the user to push policy information.
The Legacy user flow may seem simpler, but it had a few fatal flaws:
- Requires user to fill out entire form, regardless of if they are using all functionalities
- Required the back end to perform multiple tasks, resulting in longer loading time and increased risk of error.
- Required entire form to be edited when anything changed.
The updated user flow I designed allows the user to complete the bare minimum for any single task they wish to complete. This reduces the amount of time it takes for a user to accomplish specific tasks, decreases loading time, and decreases the likelihood of errors when completing any single task.
Additionally, returning to edit any specific element is simple and fast — and doesn’t require the user to swim through a large form that they are mostly leaving alone.
By keeping all of this within one form — we were forced to make a lot of calls — and it took time. Especially since the initial integration setup had to be completed before steps 2 or 3 could begin.
Breaking up the form into three smaller forms wasn’t difficult — the question was, “how do we access these three forms?”
But first, coverage rules
There was a lot of confusion surrounding coverage rules. There was the concept of both partner rulesets and custom rulesets and the original designs had a switch that allowed the user to turn on coverage rules as a whole. They were then able to add custom rulesets. However, we found out we needed the ability to turn partner and custom rulesets off/on independently.
We solved this by moving the switches to the individual types of coverage rules as opposed to the section as a whole.
This allows us to collapse sections when they’re turned off, resulting in a cleaner, simpler UI (and less cognitive load for the user).
Accessing the smaller forms
The other two forms that were pulled from the mega-form were left almost entirely alone. The main challenge that remained was figuring out how the user gets to these forms.
For reasons I will get into later (check out the Scalability section of this case study) — I opted to use a table to show the multiple actions. And within the table, I included both an edit link (icon button) as well as a switch. I wanted to expose the ability to turn these options on/off directly on this page to speed up the workflow for the user — but I needed to still allow users to edit the details.
Additionally, because some of the requests that are possible can still take some time, we brought back a few little bits of that progress bar in the form of status lights within the rows.
And finally, it’s not possible to turn on the second two options in the table if resident data pull isn’t enabled. Because of this switch interdependence — we had to make sure that when that is off — we both disable the other switches, as well as communicate to the user how they can use those switches.
(Resident data pull is enabled by default, so this state requires the user to actively turn of resident data pull, which is unlikely.)
In order to avoid the potential initial confusion of landing on the above UI — we opted to keep our old entry point. The primary button style focuses the user’s attention on the most important action, “Set up integration.” Once the integration is set up, we bring them to the new UI with “Resident data pull” automatically turned on (to allow access to full functionality).
Accessibility and scalability
Accessibility, design system edition
One of the most effective ways to improve the accessibility of a product can be to make sure you’re using as many reusable components from the design system of your organization (assuming that you address accessibility at that level). As the owner of the design system, I know that our component library, although still young, only contains components that were designed and built with accessibility in mind.
My first pass through the existing designs involved simply updating everything to the most recent component version — as well as updating all spacing to meet our updated guidelines.
This was a rewarding way to see the impact of the design system I had been working on — and also helped solve some minor accessibility issues in how certain existing components (or one-offs) worked.
Throughout the versions I worked on — I quickly realized that we were going to be adding a LOT of functionality. Every few days I’d get another message from my copilot Pankti (an AMAZING web developer) along the lines of “hey Aaron, guess what, we’re adding something again…”
Because of these constant additions, I wanted to set up the designs in such a way that adding new functionality didn’t require us to rethink the existing UI — and this led me to the table UI that is being built now.
By using a table, adding new functionality will often only require the addition of a new table row.
In addition to actions that require user input — and allow the user to accomplish tasks — we also needed a space for actions that require the user to view information, such as changelog, or to send reports to their email.
I wanted to keep these two types of actions separate. By keeping them separate I hoped to avoid an endlessly long table of actions that weren’t likely to be related. I also wanted to start carving out a space within our UI globally for specific types of actions.
Within the existing flow, you completed everything within this form — and then were landed back on the property owner page. The only way to change information was to edit the form and resubmit (and be forced to wait 10+ minutes again). Additionally, earlier versions didn’t support enabling policy push — so this was net new functionality.
I opted for global and passive actions to be directly underneath the header of the page. These actions should be easily accessible and I didn’t want them to be hidden below the fold. In our existing UI, two of the global functions (Edit and Delete) were part of a two-button FAB that has proven repeatedly to be problematic for users, as well as a functional burden for engineers. My hypothesis is that moving both of those global functions up into this action button group will increase the speed in which users can find these actions.
I opted for table rows for more task-based actions because of the flexibility built into our table rows. Although our current actions are all switches (and an edit button per row) — there is always the potential for future functionality to require other means of interaction. A table row can support most means of interaction within our system.
As we build out this feature, I am working with our engineers to try and measure how much more efficient the new design is. By breaking up the mega-form, we allow the users to complete the minimum amount of actions before submitting anything. This allows us to make fewer calls per UI. Additionally, there will be many times when not all three switches are turned on – in these cases, we’re always saving the user time by not forcing them to both fill out the form regardless, as well as not making extraneous calls.
The design decisions we made for this feature reduced the amount of time necessary to accomplish core tasks by more than 50%.
This translates to money saved for the business and headaches saved for the users.
When we demoed this to one of the primary users — he said, “Cool, no questions. This is going to save a lot of time.”
There’s no greater compliment in my opinion.