This ER admin staff scheduling company wanted their popular app brought into the modern era! With the help of visual designer Logan LaBo, we designed and implemented a sleek new interface!
The client's goal was to preserve the functionality while modernizing the application and improving user workflows on the frontend where possible without backend changes. We approached this challenge with the following plan:
Throughout the process, with help from my visual design buddy Logan, we were able to formalize design concepts and gather feedback from the client and iterate where needed. There was a lot of creative freedom, and we leveraged that to tackle some key obstacles.
One of the views critical to the application is the Schedule Modification view. This administrative view displays a Schedule
consisting of individual Shifts
on each day, each with an assignee, and enables various administrative actions.
The Shift Calendar
ended up being the most complicated and feature-rich component due to the fact that a lot of the views presented information this way, and we wanted a consistent experience for the user. In the existing UI, the Shift Calendar
is presented as a table where each cell is a Shift
and admins can take actions on a Shift
by clicking on the cell and then selecting an action.
In the new design, we replace the tabular display with a set of collapsible containers for each week, and allow hover interactions to expand the Shift
actions. This creates a more dynamic space for the user to work in and gives us a more modular visual framework to use in the other views where the Shift Calendar
is presented with different feature sets.
As an admin audits the Schedule
and makes assignment changes, they need a way to visualize which Shifts
a worker is assigned, when workers are unavailable, and even if they have put in a request to work a Shift
. The Shift Highlight
feature makes this possible with Highlighter
indicators, however the old solution presented the user with potentially crowded visual information, leading to increased cognitive load. The information would be compounded when multiple workers were highlighted, which made it more difficult for an admin to work effectively with the view.
In the new design, there is a more subtle approach and consistent visual treatment between the primary and secondary highlighted workers to minimize the cognitive load for the admin. This approach allows them to understand worker status at a glance and get more information deliberately by focusing on a particular shift.
Shifts
are organized into Shift Groups
which can be used to filter the displayed Shifts. The existing design uses a simple and rigid checkbox-filled table to enable filtering.
We were able to improve the UX of the Shift
filtering feature by introducing the concept of dynamic MultiToken Selects
inside of a popover to replace the existing expansive and rugged design.
The server application was originally built in 2013 using Ruby on Rails and largely maintained on the original versions of Ruby 1.8 and Rails 2.3. The existing frontend was largely built using vanilla HTML/CSS and JavaScript with jQuery. For the new frontend, we proposed introducing some frameworks that would make development more efficient and save my client money in the long run. We arrived at the following stack after researching frameworks that would be more compatible with legacy Ruby on Rails projects and minimize friction with the existing development team's experience:
In order to use some of these frameworks though, we needed to upgrade the backend several major versions. We intially implemented UI Components in a sandbox in parallel with ShiftGen's backend engineers until the minimum requirements for the new frontend were met. We periodically helped with debugging and gave advice on prioritization of issues based on what was in scope for the frontend changes. Once the backend stack upgrade was completed, we ported the Component code into the existing application, which presented some new challenges involving the new Rails asset pipeline.
Now that we had a basic set of components integrated into the application, the refactoring began. The basic refactoring pattern was to use modular SSR ViewComponents styled with Tailwind and inject any necessary interactivity using Stimulus. Due to the high degree of coupling between the frontend and backend in the legacy code, refactoring several times was often necessary. We eventually found decent, reusable paradigms to replace all sorts of existing legacy micropatterns: CSRF token injection, form submission actions, and even interpretation of vanilla JS server responses.
Ultimately, after logging over 350 development hours, we touched more than 300 files and wrote over 11,000 lines of code to complete the project.
When this application was originally written, it was done incredibly simply, which minimized its client-side footprint. One of the notable changes after refactoring and implementing the new designs is the fact that the sheer scale of the size of the client-side HTML and JS code is an order of magnitude higher than before. There is significantly more rendered HTML code due to the use of ViewComponent and Tailwind (more complex layouts, repeated class names, etc.) and quite a bit more interactive and dynamic code execution due to the improved UI/UX decisions.
With a modern computer and browser, the average Schedule
rendered and performed just fine in initial testing. However, it became clear that the outlier Schedules
with more than a few hundred Shifts were slow to load. Load time was also a major issue on the All Site View, which presented data from multiple Schedules
all on the same page. And so, we put on our code performance hard hat and started to dig.
Let's define Load Time as the time it takes to enable smooth user interactivity with the UI. This is a very important metric to optimize, as it directly affects the user experience and can be a major source of frustration.
By observing load times for different schedules, we can infer performance scales roughly linearly with the number of ShiftCell
components.
We see that there are two parts to the delay:
We cannot dig deeper yet with the current implementation, as it is impossible to complete a Performance monitoring run in Chrome dev tools. From this we can infer that there is a significant CPU draw on the client after the server response.
Initially, the Tailwind classes seemed to be quite verbose, so steps were taken to move lower-level component Tailwind references into the component-specific Tailwind @layer
. This reduced the response size by ~20% but did not seem to make a huge difference in the load time.
Downloading the page's HTML and analyzing node sizes, we see that the Options
component HTML for all the Availability dropdowns in each ShiftCell
totaled about 100MB. Removing these from the response reduced the response time by 40% and reduced CPU usage enough that Performance monitoring was able to be completed, but notably did not reduce the render time.
We also noted that the original implementation only included Availability on shifts assigned to the logged-in user, so we could actually make these render conditionally. This change alone is already a huge improvement.
After reviewing some Performance monitoring runs, we see the render time is largely comprised of querySelectorAll
calls. With some research, we find that those calls were being made by the Stimulus framework in order to register ShiftCell
outlets for the ShiftCalendar
controller when it is initialized. Since these outlets are not needed during the initialization period, we use another mechanism to reference the ShiftCell
controllers dynamically (for filtering/highlighting). We also take a final step to avoid applying filters when they were initialized as it was a no-op.
The new mechanism is significantly more efficient and reduced render times to just a few seconds!
This project was a great experience refactoring a large, legacy codebase while modernizing the tech stack and UI/UX of the application. It also came with unique UX challenges that were satisfying to address, and technical challenges that required thorough research and debugging skills. We were able to provide a holistic solution that improved both the UX as well as any further design and development roadmaps for the application, and the stakeholders of the project were quite pleased with the results!