When over-engineering makes you forget about what really matters
My team and I recently worked on optimizing the performance of one of our SaaS applications, in order to reduce infrastructure costs, and improve overall user experience by cutting down loading and rendering times as much as possible. We explored many options and tested different frameworks. Server-Side Rendering was one of the techniques we especially focused on. Here is our feedback regarding this solution. As the title suggests, this article is quite one-sided, however I tried to rely on factual data as far as possible, so that it is not just a debate of beliefs.
What is Server-Side Rendering?
And that’s pretty much it. The whole DOM is built dynamically. It makes a fundamental difference with older methods, where pages where 100% statically generated, server side, and where the browser just had to render the raw output. This trade-off between server and client has several important implications:
- Computation is now mostly done in browser, freeing a lot of load from back-end, reducing their usage costs and increasing the amount of traffic they can handle.
- It is possible to create fully interactive pages, with a much more deep user experience than with static pages while avoiding successive reloading.
- Performance/UX: the fact that pages are not sent back ready to be displayed to end user makes it potentially longer to load. Especially when network connection is bad, or device computation resource is limited. We’ll come back to that point later.
In order to get the best of two worlds, developers thought of a new hybrid system, called Server-Side Rendering (SSR). As its name suggests, the principle is to render the page first on server, before sending it, and then bring all interactivity within it by a process called “hydration”. This way, crawlers see plain HTML that is good to index, users get their page ready to be displayed, but still benefit from all interactivity brought by reactive frameworks.
Challenges brought by SSR
Theoretically, it sounds like a great idea. But in practice, while trying to implement this system, you will find yourself facing a bunch of new challenges, that can require entire teams for days, and can cause many headaches to solve. In his article, stereobooster makes a pretty accurate list of those concerns you will have to deal with. To sum up, you will have to ensure that the following is also working seamlessly on back-end:
- Dynamic data fetching from an API or any web service before rendering
- Using a front-end router, or internationalization (i18n) utilities
- Using HMR for development
- Lazy loading components
- Using a state manager like redux or diox
For each one of these, libraries, hacks or workarounds are available. They have nonetheless a lot of limitations, and you will probably spend a huge time adapting them to your own use case. So, is implementing Server-Side Rendering in your apps really that worth? To answer this question, let’s dig further through the different drawbacks SSR tries to solve.
As I previously mentioned, without SSR, your website’s indexing won’t be impacted on Google or Bing. But it will most likely be on other less advanced crawlers. Before trying to fix that point, the most important question you should answer first is: do you really need SEO?
If you are building a web app, odds are that this app will go through an authentication system, will display user info that must remain private, and its content will always vary from user to user. In such case, the later question is a no-brainer, you don’t even have to think about indexing your pages (and probably don’t event want to).
In more specific cases, you may want to index your website (e.g. e-commerce, list of public profiles, …) while providing great experience to end users. Despite of that, implementing SSR yourself is not the only option. Several easy back-end solutions are available out there, for free, like Pupeteer. A simple trick consists in checking either current visitor is a robot, and if so, pre-rendering the app before sending a full, raw HTML. SaaS services can also help you at this task, as prerender.io. It can be worth having a look at them, depending on your needs.
On a more philosophical perspective, being able to index dynamically rendered pages for a crawler is now the way to go, and almost a prerequisite. More and more websites are created this way for convenience, productivity and all the benefits it brings. Search engines will have to adapt to that global trend in order to stay competitive. So, spending a huge amount of time trying to improve support for less advanced robots feels a bit like keeping your site IE8-compatible, isn’t it?
Performance & User Experience
This part is a bit more complex to determine, as it varies a lot from device to device, and depends on both network’s quality and performance. Firebase team made a very interesting series of videos speaking about how essential SSR is and how it dramatically improves your app’s performance and reactivity. But if we think about it from another angle, what do we learn? When it comes to improving pages loading times, only 3 KPIs matter. FP, or First Paint, this is the time it takes before the browser actually displays something to the user. FCP, or First Contentful Paint, is the delay after which the page AND meaningful data are display to the user. And finally, TTFI, or Time To First Interaction, is the time from which user is able to interact with the page (e.g. clicking buttons, navigating, …).
As we can see, improving UX on page load basically consists in improving each one of those KPIs as much as possible, so that end-user can see and interact with its application without waiting.
From the diagrams above, we can notice that:
- With SSR, page takes much more time to load because API calls are performed on back-end, before sending any response to the client. During that time, end-user is just waiting in front of an white screen. However, once everything is loaded, useful content is displayed sooner than without SSR. Regarding TTFI, as all assets are required and DOM must be hydrated, user sees a complete page, but he cannot interact with it for 2.5 seconds. It makes the app feel cumbersome, laggy, and can generate frictions.
UX-wise, this example shows pretty clearly that SSR itself is not especially better or brings any valuable improvement. Moreover, you can do better without SSR. How? By optimizing your UI components, assets size, by lazy-loading unnecessary elements, and so on.
Costs & Complexity
Besides the two main concerns we previously discussed, there is another one that developers don’t really talk when promoting SSR: the costs that inevitably come with increased complexity.
- Enabling SSR requires to rewrite a large part of your app so it is 100% isomorphic (although you never completely achieve that). It takes a huge amount of time, that you could have spent improving other aspects of the app or focusing on other ways to increase performance.
- You will definitely need a more powerful and complex infrastructure, as making an app work on back-end has many implications in terms of security and features.
- Your servers will be able to handle much less traffic than before (because of the computational resource it takes to render pages), increasing your costs.
Conclusion — About simplicity
I personally like very much the saying YAGNI, or “You Ain’t Going to Need It”, because it makes totally sense, especially in software engineering. Developers love finding new problems, coding awesome stuff and framework that beautifully solve them, for the sake of technological achievement itself, more than the benefits it actually brings to end-user. I believe that SSR is one of them. It makes you loose focus on what really matters, your users. It’s another technical challenge that brings infinite discussions and costs so much time for such a small ROI.
In any case, if you are interested in exploring Server Side Rendering for you apps, I recommend you the most popular open source frameworks out there: