Spend any time in web development and you will be struck by the daunting pace at which the technology landscape changes. The must-have technologies of today quickly become the legacy spaghetti code of yesterday. In some cases, adopting new technology is as simple as adding a new library. Other times, large scale architectural changes need to be made. For those looking to move from traditional server side MVC apps to newer client side single page apps, the migration path is not easy nor clear cut.
Here at Bitsight, we are taking on this challenge in a structured, manageable way. In this article, we hope to share some of our lessons learned.
Why Choose Single Page Apps
Why did we decide to go through all this trouble in the first place? The primary reason is that single page apps offer an enhanced user experience by being more responsive and dynamic. Through only rendering sections of the current page, we do away with the feel of traditional web apps, with their frequent page reloads, and move closer to what feels like a native desktop or mobile apps.
Single page apps also offer opportunities for performance optimization by reducing the number of network requests made to the server. Without clever caching strategies, traditional MVC apps may be subject to redundant requests and renders of shared resources across their many views.
Finally, the process of this migration has forced us to improve our code structure. We are moving toward a better separation of concerns between the browser and server, as well as enabling UI reusability through well-defined components.
What Needs to be Done
Traditional MVC apps are driven by server-side route handlers which render separate views for each URL. In Bitsight’s case, we had multiple views/pages driven by a Django app server. Every time that a user navigated to a new URL, a round trip to the server was made and a new page rendered. The migration to a single page app means planning for a complete change in this behavior. In a single page app, the route handling is moved to the client side. At Bitsight, we made the decision to move to using React/Redux and its associated route handler. In this new framework, every time the client navigates to a new URL, the code dynamically re-renders portions of the existing page, and new requests to the server are limited to API calls. As a result of this change in control, we gain the flexibility of rendering and delivering a much smaller set of data per navigation.
What We Learned
Here are five things that helped smooth our transition to the single page app.
1. Pick the Right Framework
It can be trickier than you'd assume to pick the right framework. Internet searches will lead to many different opinions about which is the "right" framework. At Bitsight, we decided to go with React and Redux for the following reasons:
- It has a strong ecosystem of corporate and developer support.
- The syntax and structure of the code leads to better module structure and organization.
- When using the framework, there should be a reasonable expectation that given a complex UI, an implementation using the framework will perform well.
2. A Well Defined API
Single page apps are heavily dependent on well-defined APIs for data retrieval. At Bitsight, we were already busy crafting a public API for our customers to use for their own app development needs, so our transition to a single page app became a good case of ‘eating your own dog food.’ As we built out our app, we were able to find deficiencies in our API and fix them. If your organization does not have a strong API or SDK, you will want to start planning that out before you begin transitioning to a single page architecture. It can be helpful to involve your frontend engineers in the process of creating these APIs. Otherwise, your developers may find themselves quickly blocked by the lack of access to data.
3. Define a Container / Component Structure
In a single page app, sections of the UI are replaced dynamically based on user interaction. Planning which parts of the view are dynamic will go a long way in helping you organize your modules. This is where working with your design teams to come up with well-defined wireframes will speed up your development process.
Additionally, planning a good repository for reusable components is a good idea. If you plan on putting your components in another repository, agree beforehand with other developers how versioning will be handled. If you don’t plan on using a shared repository, make sure that the reusable modules of your app are in an easily accessible place.
4. Understand Your Build Tools
For all these reasons we chose Webpack as our build tool of choice. Webpack has the additional benefit of speeding up our development through the Webpack development server, a lightweight server which automatically reloads your content based on your changes.
5. Incremental Changes
If you are a developer looking to convince stakeholders of the need for a conversion to a single page architecture, you will need to be able to convince your stakeholders that there is an incremental approach. Such an approach is preferable to a wholesale rewrite to reduce risks to the product, etc.
Here at Bitsight, we developed an incremental approach where parts of the app are migrated to the single page app in separate releases. This is done with clever URL management. We defined a new sub-route/app, which hands all traffic to the React/Redux router. For example, if the old dashboard page (/dashboard) is ready to be owned by the single page app, it is converted and then given a new URL/app/dashboard.
We try to be data driven whenever possible at Bitsight. Though general measures of user experience can be subjective, speed measurements can help discern if there is a positive impact on the user’s experience. Below are the results of some of our efforts on our first converted page:
- 32% reduction in http requests
- 66% reduction in time to first render
Note that the final load times are very similar, since most of the load time is spent retrieving data from an API. The really big win here is the DOMContentLoaded time, which is the time required for something user-visible to show up on the page. With our migration completed, the load time was only 590 ms, which is over twice as fast as the original 1.72 s. Also note that though the total load time are similar, this data represents the initial page load, which retrieves a lot of the structure needed to support the single page app. Each subsequent page navigation will not suffer this penalty as only a section of the page is reloaded.
For any organization looking to dramatically improve their product performance and user experience, moving to a single page architecture is definitely something to consider. The migration may seem like a daunting task, but we hope that this article has shown that it can be done in a well-planned and structured way.