An Unexpected DNS Error

Cloud Computing
WebDev
Optimizing payload with serverless deployments of a NextJS website on Vercel.
Author

Kasra Lekan

Published

September 1, 2024

A few days ago, I went to my freshly redesigned website having not viewed or edited it since early the previous day. I received a DNS issue as shown in Figure 1. This error was gone if I refreshed the page. However, most people who get an error going to a website will assume that the website is down so the error needed to be addressed.

The result of viewing my website for the first time in ~24 hours before attempting to address the issue. Refreshing the page caused it to resolve.
Figure 1

Diagnosing the Problem

I knew that refreshing the page resolved the problem and that the issue would not arise if I re-attempted to view the website soon after visiting it. This indicated that the problem likely did not have to do with standard speed benchmarks that we concern ourselves with in web development, e.g. first content paint. Nevertheless, I wanted to rule out first content paint being an issue so I did some speed testing on the deployed website. As expected, the times were great with the first content painted within .54 seconds and the entire website loading in 1.5 seconds.

I was unable to find any documentation of others having this problem despite my having a fairly standard NexJS website without a sprawling dependency list or complicated logic. I had deployed many websites before with Vercel with no issues so I knew it was something with my project that was different than my previous ones. I was using two packages that I had never used before framer-motion and react-rough-notations. I considered the possibility that these dependencies may be the culprits because they drastically slowed down compilation when I was developing locally, taking roughly 5 seconds to load content after a fresh restart with next dev.

Based on my observations, I decided to focus on optimizing the bundling of my website packages in any way I could. In the back of my mind, I thought that these issues should not cause the DNS error I saw before but I had no other theories and had to start on a solution.

A Digression on Serverless Computing

Vercel1 deployments are, to a first approximation, a layer on top of AWS Lambdas, a serverless computing provider. While I have not worked on any large-scale projects that provisioned large cloud systems, I studied them during my Masters and they are perhaps the most underrated technical achievement fueling the Internet today.

In general, cloud computing provides for economies of scale reducing the cost of server management and the aggregation of the best technical expertise. Serverless functions are another layer of innovation on top of cloud computing. They are extremely optimized down to the kernel level so that they can cold-start with 100s of milliseconds or even microseconds of a request. Thus, serverless functions can start at request time rather than running permanently (Figure 2). The reason I don’t have to pay for deploying my “hobby” projects (with low traffic) is that it costs virtually nothing2 to run these serverless functions.

Figure 2: Serverless computing (FaaS) visualized. Image credit: Prof. Yue Cheng

Solutions

The initial bundle analyzer output showing the size of the various parts of the website after building. NodeJS server results are shown.
Figure 3

Based on the documentation and the results of the bundle analyzer, I did the following:

  1. I added a Suspense wrapper to my website’s content with a loading element.
    • I knew this would not solve my issue since my first-paint speed was good, but using Suspense to have a loading UI is best practice.
  2. Added the optimizePackageImports flag and applied it to framer-motion and react-rough-notations. From the docs, “This option will only load the modules you actually use, while still giving you the convenience of writing import statements with many named exports.”
    • I was especially interested in its performance with framer-motion since the dependency JavaScript is large and there is not much that you can do to reduce it3 since you cannot import specific animations.
  3. I converted framer-motion calls to use vanilla CSS for simple animations.
    • I used framer-motion when vanilla CSS could accomplish the same effects since I already had the dependency. I realized that I could remove framer-motion from the necessary page load by using vanilla CSS when paired with the next optimization.
  4. I applied component lazy loading and skipped SSR4 for my animated components based on the docs.

🛬 Results

The final bundle analyzer output showing the size of the various parts of the website after building with optimizations. Optimizations reduced the parsed size by ~19%. NodeJS server results are shown.
Figure 4

After applying the optimizations my server-side bundle size (parsed not static) was reduced by 19.1% as shown in Figure 4. Since these updates, my website has worked properly even after prolonged periods without requests.

🚀 Why not Astro

When first building my new website, I attempted to use Astro instead of NextJS. Since my website is all static, Astro would perhaps be a better technical fit especially since their implementation of Server Islands which is similar to NextJS 14’s Partial Prerendering would allow me to adds dynamic components when necessary. There are a lot of things I love about Astro. However, I struggled to use stateful components in Astro and ultimately had to use Next.

🪞 Reflections

The automatic optimizations that enable the rapid deployment of efficient websites like my own are truly stunning. A combination of serverless optimization, package bundle optimizations, and component optimizations in React metaframeworks allow me to focus on the content first. When I made my first website some years ago, I manually minified all my images and converted them to better formats for web viewing. With modern frameworks like Next, there are built-in Image components that perform optimizations like this for you.

We stand on the shoulders of giants.

Footnotes

  1. My website is built with NextJS so Vercel was the natural choice for deployment.↩︎

  2. Pricing for AWS Lambdas are billed at the 1-millisecond granularity. As of writing:

    • $0.20 per million requests
    • $0.0000166667 per GB-second of compute

    This implies running 6000 1 GB Lambda function for one second costs $0.10.↩︎

  3. This is not entirely true. There are some tools Framer provides to reduce the bundle size but they have limitations. As Framer points out, this normally shouldn’t be necessary because most bundlers apply “tree shaking” to reduce the bundle to just what is used.↩︎

  4. Server Side Rendering (SSR) refers to when the HTML that the client is served is generated on the server, often using a database or other external APIs.↩︎

Citation

For attribution, please cite this work as:
Lekan, Kasra. 2024. “An Unexpected DNS Error.” September 1, 2024. https://blog.kasralekan.com/ideas/optimizing-serverless/.