Your SSR is slow & your devtools are lying to you
Tags: javascript, performance
Published: 2022-03-22 22:12:29

As developers we want our sites to be fast, and it takes many small wins to add up to a performant site.

I want to talk specifically about two performance factors, and how your devtools might mislead you to believe they're not worth pursuing, leaving your users with a slower experience. Those two factors are **rendering** and **streaming**.  

----

## Rendering

Let's start with rendering.  The reality is, many of us are building websites using tools that are primarily focused on client-side updates.  It's typically easiest for these tools to replicate the browser environment on the server to generate the initial HTML, so that's what many of them do - whether it's a full-blown headless browser, jsdom, or a virtual dom.  

On the lighter end of this spectrum (vdom), the performance is typically considered "good enough", where that's often tens of milliseconds, compared to a purpose-built, string-based HTML renderer that may be more like 1ms.  

These same frameworks also perform a process called "hydration" which typically involves serializing a _lot_ of data down to the browser to make the page interactive.  This serialization process consumes valuable cpu time and further delays the response.

Okay, but does it really matter if your page takes an extra 50ms to load? Maybe not.  But what about concurrent requests?  See, rendering is a cpu-bound (blocking) task: if rendering takes 50ms and 10 requests come in at roughly the same time (to the same render process), the 10th is left waiting for 450ms before it can start doing its work and respond.  Looking at the response time of a single request isn't giving you the full picture.

![blocking render](https://user-images.githubusercontent.com/1958812/159560970-a5ca4834-0f78-4b54-8293-03a0ee3f110a.gif)

---

## Streaming

Next up, streaming.  Specifically, early flushing of HTML before we have all the data necessary to render the entire page.  If you don't stream, your page is going to be as slow as your slowest upstream dependency.  Streaming decouples your Time to First Byte (TTFB) from your data sources and enables the browser to start rendering and fetching known resources earlier.  Depending on the speed of your upstream data sources, this can have a significant impact.

It doesn't only affect your TTFB but hastens First Contentful Paint (FCP) allowing the earlier display of available content and loading indicators. And depending on how broken up the page is, it also allows hydration to occur earlier and piecewise. Ultimately streaming can have a positive impact on Time to Interactive (TTI) as well.

Even if your data sources are pretty fast, at worst your content ultimately reaches the end-user at the same time. But when your API, database, or network hits an outlier on latency, streaming has you covered.

![streaming render](https://user-images.githubusercontent.com/1958812/159561135-d2571876-5743-49ae-bc6b-7c8c636e35a1.gif)

---

## Emulating Slowdown in Devtools

If you're testing performance, you typically want to understand the worst-case scenario.  Everything's going to look fast for the person on the Mac Studio M1 Ultra with 10 Gigabit Ethernet.  No, you want to understand how your site feels for the person on an older android device over a spotty cellular network. And that last part, the slow network, is where we run into trouble.

The way devtools emulates slow networks _hides the impact of server-originated delays_.  If we dig into what the presets, such as "Slow 3G" and "Fast 3G" are doing we can see why:

![network throttling](https://user-images.githubusercontent.com/1958812/159048789-18eceb31-c0ad-45bc-85ba-b6113fa3c286.png)

You'll see here there is a "latency" setting, which ensures the request takes _at least_ that long, but...

> DevTools doesn’t add an extra delay if the specified latency is already met. 
>     - [Matt Zeunert, DebugBear](https://www.debugbear.com/blog/network-throttling-methods)

What? So on "Slow 3G" where the latency is 2s, that means whether the server responds instantly or takes the full 2 seconds to respond, devtools shows the same timing for those requests? **Yup**.

You can try it yourself. Take a look at the timing for these two pages without devtools network throttling and then with "Slow 3G":
- https://restless-cake-d61e.mlrawlings.workers.dev/?ttfb=0
- https://restless-cake-d61e.mlrawlings.workers.dev/?ttfb=2000

---

## Takeaways

You'll notice I didn't include any hard numbers in here.  Things like server architecture will make these factors more or less relevant. Do your own testing on real devices & networks. Even more so, look at what your _actual users_ are experiencing—especially at the [long tail](https://timkadlec.com/remembers/2018-06-07-prioritizing-the-long-tail-of-performance/).

It doesn't help that we're often locked into a certain class of SSR performance before we ever get to the stage of testing these things.  If you've built your app using one of the afore-mentioned client-focused tools, you may have to reconsider that decision or hope you can find enough wins elsewhere.

While there may be other factors impacting your site's performance, making your server respond faster is only going to improve things. And don't let your devtools fool you: if something's slower over a fast network, it's going to be slower over a slow network as well.