What is the SUF Stack?

by Brad Ito

I've been building web apps for a while, and was never quite happy with the trade-offs involved in the choice of architecture. Frameworks and architectures would only really optimize for one of: users, developers, or infrastructure, and compromise on the others.

Now, there's a combination of technologies that when combined into a single architecture combine to combine to avoid many of those trade-offs. I've been using this in production for a few years now, and have found it effective in quickly building performant, scalable web apps.

This site is about sharing the SUF Stack architecture with other creators to help enable them to create great things.

A SUF-Stack app is:

Serverless

A SUF-Stack App should be optimized for its deployment on the cloud. This means optimizing costs to only pay for the compute which one is using. And it should mean worry free server provisioning and maintenance.

We've been using the Serverless Framework. It configures deployment of apps to deploy as HTTP endpoints that call function-as-a-service (FaaS) code on a variety of cloud providers. It also provides a rich ecosystem of plugins and tools which one can leverage to expand its capabilities. In the AWS context, it actually configures AWS CloudFormation, and can thus be extended to configure pretty much anything on AWS.

The starter template deploys to AWS Lambda, behind AWS API Gateway. It also deploys static assets on S3 (using content-based hashes in names to bust caches) and leverages CloudFront to both serve content close to users and to only send HTTP requests which need a dynamic evaluation to the backend in Lambda. CloudFront is configured to use HTTP/2 for faster transport, and to take advantage of compression to further reduce bandwidth usage. Furthermore, logging is written to AWS CloudWatch in JSON, for parsing in CloudWatch Insights dashboards.

FaaS-based HTTP endpoints have a known issue of taking longer to serve requests if the function has not been called for a while. To solve this cold-start problem, and to provide better uptime monitoring, we configure an AWS Route 53 HealthCheck to repeatedly query the backend from a variety of global locations which keeps the lambda function warm. We also deploy the application as a single function to simplify routing and so that there is only a single function to keep warm.

Known Limitations:

- Web requests must not take longer than a few seconds (30 seconds for AWS). This is also a good practice for human-facing user experience, and you should consider a different backend infrastructure to run long-running processes.

- The bundled application should not be too large (250MB for AWS). Which means that your application code should just be code. Data should be stored in something like a database. And images and video should be stored elsewhere and linked in to the application. I'd recommend a service like cloudinary for images and video.

- Streaming HTTP services like web sockets and WebRTC will require, a different, more specialized, deployment.

Universal

A SUF-Stack app should be optimized for its users. This means being a universal application where the first page has HTML generated on the server, for the fastest possible render, and where subsequent pages are generated on the browser, leveraging the techniques from single-page apps. Being universal helps with automated parsing of your web pages, as for search engines and other web spiders.

In addition, web application code should be delivered with long cache times to speed up later visits to the web application, with those bundles named automatically with hashes of their content so as to do cache busting in a natural manner.

The starter template uses Nuxt.js to orchestrate webpack to build universal applications. This means that you just need to focus on writing your app and Nuxt.js will take care of wiring it up into a universal app, without you having to worry about how to make that happen. There are some subtleties about making relative URL resolution consistent which the starter template takes care of for you.

Another great feature that Nuxt.js exposes from webpack is the analyze build option which lets one visualize how many bytes your code and library dependencies are using. One should use this to guide optimizing your web app size to speed things up for your users. There is also a great variety of Nuxt.js modules which one can use.

Known Limitations:

Some UX-related packages only work in the browser, and may need to be loaded via the Vue mounted hook

Some bugs may be harder to trace when your app is in universal mode. You may need to temporarily change your app to a single-page-app to debug.

Universal apps run code every time that a page is requested. This lets a web application show different content to different viewers, but it is not as efficient as a static page. If you are only rendering static content, you don't need to build a web app, and should use a static site generator.

Full Stack

A SUF-Stack app should be optimized for developers. That is, it should minimize the number of languages needed to write the full application to just Javascript (or Typescript).

It should have a clearly defined frontend using a mature framework, so that one can count on finding answers to common problems. There should also be features such as hot-reloading during development and a robust set of options for CSS and UX component frameworks.

A major differentiator between a SUF-Stack web app and a Jamstack app is the use of a proper backend in SUF. This means either Express.js or Koa.js or similar backend designed to be run server-side, with access to secret credentials, and priviledged code. It's often simpler and easier to just write your own backend API routes using Node.js code, especially when interacting with databases and external APIs.

The starter template puts backend code into its own folder, and optimizes the development experience for it. The backend is deployed as part of the same serverless function as the frontend but with priviledged values (secrets) available to it via environment variables. One can develop the frontend and backend for a given feature in the same code base, and ensure that they are always deployed in sync. For development, the frontend and the backend execute locally for a fully functional local development environment.

Known Limitations:

Not all developers or applications need a backend. For those situations, it may not be necessary to use the SUF-Stack.

Next Steps

If the SUF-stack sounds like what you need, then you can get started today!


Some History and Context

The first websites were hand-coded HTML files hosted on individual web servers. For creators of those sites, it often meant lots of markup repetition and other inefficiencies. For users of such sites, the content would tend to get stale and lose relevance. For the system administrators in charge of the infrastructure, there would be a worry about keeping the sites online, especially when there would be a surge in users for a popular site.

Next came the first generation of web applications. These provided dyamic content via server-side code, and unlocked the web as a vehicle for eCommerce, search engines, and more. Users were delighted by the new functionality, and would tolerate the sometimes slow loading times. Creators were empowered by their new capabilities and eagerly dove into a raft of new languages and frameworks. However the infrastructure demands became complex and error-prone.

Single-page apps emerged next, offering much richer experiences for users by leveraging browser capabilities to react quickly to user inputs, and unlock emerging web standards. However, for creators this created the need for different languages and technologies for the frontend and backend, resulting in diverging skills and split teams. Infrastructure needs, which had only started to simplify via public cloud providers, became complex balances between versioned deployments of the frontend and backend, and the unique scaling needs of each.

The next set of web application architectures solve specific weaknesses. Universal apps which render content both on the server and in the browser aid in search discoverability and speed but suffer from complex deployments. Static site generators sacrifice dynamic backends to optimize for distributed and cached infrastructure, and fast page speeds for the user. The Jamstack adds more dynamic content to static sites, but still suffers from a lack of a backend with priviledged code.