hand sketched logo of electrons orbiting a nucleus

Reading: Navigating the Future of Frontend

Random notes from Navigate the Future of Frontend

capability vs suitability

theres a tension between tool being highly capable and highly suitable. by suitable we mean well fit for a specific domain/purpose/context.

the trade offs are NOT 1-1 tho. there are subtle paths where you gain capability without losing suitability, and vice versa. so this is tricky.

compute and storage

the client has limited compute, but it happens right in front of the user providing immediate feedback.

the server has wide, parallel compute, but it happens far away from the user, causing network latency.

the store is similar for storage.

the client has limited storage, but it happens right in front of the user providing immediate access.

the server has wide, parallel storage, but it happens far away from the user, causing network latency.

distributed frontend

we write code in one repo and deploy to client and server

next-gen bundlers make this possible

suspense

when we move to thicc clients, we had to suspend rendering until we had all files, we parsed all files, executed code, then rendered (and maybe even fetched some data)

this caused TTFB (time to first byte) to be high and a slow LCP (largest contentful paint)

with suspense, we can have phased loading stages -> instead of a popcorn of loading spinners.

RSC (React Server Components) can stream virtual DOM for in-place transitions.

aka suspense solves the problem of consistent rendering when it is async and can be streamed in any order

this can give us "partial pre-rendering"

suspense + RSC + next-gen bundlers = partial pre-rendering

framework-defined infrastructure is the idea that the framework can define the infrastructure for you. this is a big deal because it means you can focus on the app and not the infrastructure.

streaming!

to avoid network waterfalls, we chose to have statically defined routes.

and once you have statically defined routes, its easy to have file-system-based-routing. (another option is a configuration file)

this creates a hierarchical tree structure of routes.

"backend for frontends" is a design pattern familiar in a service-oriented backend environment

the idea is there is a custom backend for each frontend.

this is made even more powerful with RSC (React Server Components)

BFF allows for

  • smaller client bundle by moving fetching and data transformation to the server
  • compose multiple data requirements (no more need for graphql to do this?)
  • product engineers can now specify precise data requirements (graphql like)
  • leverage url state

cache

move all this stuff out of the client means we can do more with caching!

  • public cache - where data isnt sensitive or personalized, public cdn
  • private cache - ex in-memory client side cache of remote data, or browsers native http cache

a MAJOR source of complexity in any system is state management

BIG part of the work in frontend is keep its state in sync with the server.

(we'd love love love to have local first when building applications and using stuff like CRDTs)

  • manual cache management - redux, mobx, etc
  • key-based invalidations - remove the need for manual cache management, examples are react-query, swr, relay, urql, etc

data fetching/cache invalidation/server actions

traditionally, we'd POST on form submit, redirect to a new page, and then GET/fetch the new page/data.

Remix and SvelteKit send writes with form data to route level server actions.

next and solidstart allow for server actions to be called anywhere in the component tree, this makes them more like RPCs ->

this request-response model has coarse-grained updates at the route (or nested route) level.

this is a decent default for a large percentage of experiences, however, there are times when we want fine-grained cache-management/updates/client side data loading.

TODO: when is it better to use server actions vs client side data loading?

benefits of server components

  • aligned to teams that work in full-stack vertical slices or steel threads

  • server-driven UIs are a natural fit for server components. they allow for native mobile apps to use the same server components as the web app. react-strict-dom is a good example of this.

  • generative UIs are a natural fit for server components. this happens when you need to have the data before you know what components to render, think CMS content types. also this wild example where you have AI function calling a server component to generate a UI.