Work in Progress — This post is still being written.

The correct way to think about front end applications is "What is the ideal experience for the user". I've seen too many developers from the outset consider technical limitations when approaching architectural decisions for the front end. These are not UI decisions, but how should I structure my code to be best suited to fulfill the ideal user experience.

What is the ideal user experience? Well, hard to define the totality of that, but we do know 3 major ones at least: Visual consistency, Lively data and Responsiveness.

Visual Consistency

Visual consistency says we should probably share components, and put css colors in variables, it doesn't say anything about how things look but that they should probably be consistent.

Lively Data

Lively data means ideally every server update streams live to the user, no refresh, no user action required. Now this one needs to be broken down to solve because there are some constraints of the client server model that may or may not be worth it to work around.

Responsiveness

Responsiveness means that each network request is instant, including the initial html and css. We want everything to be loaded and actionable as quickly as possible. Once loaded the website should also be responsive in the sense of responding to user actions, but this feels a bit more like UX/design than architecture to me.

The Tradeoff

Now, there is a bit of an issue with lively data and responsiveness, these two are at opposing each other in trade offs. Anytime you go to the network there's the potential of causing a loading state and affecting responsiveness.

Luckily it is a solved problem through various means. Some non-exclusive examples are:

  • Websockets for live updates - Liveliest data, little to no responsiveness effect if implemented well. Much more costly overall than http.
  • Polling - Straight forward, easy to retro-fit onto existing systems. Much more server side load for each client that necessary.
  • Front end caching - Provides responsiveness, often used with other mechanisms. Data liveliness not handled, and sometimes impacted negatively.
  • Optimistic responses - A subset of caching that works in relation to CUD of CRUD.

Abstractions

So how do I handle this tradeoff? Abstractions!

The way I see it there are three main parts of the front end: the display, the state and the network. Each of these is such a separate concern from each other that it doesn't make sense to me to couple their implementation details.

To break out each part, the display is the component that actually shows the data, a trivial example:

const User = ({name, age}) => {
  return <div className='user-card'>
    <div>{name}</div>
    <div>{age}</div>
  </div>
}

The network is the literal network requests, this can include caching, optimistic responses, websockets, all the fun solutions mentioned before.

The state is tracking the users interactions within the website, the form data, what things on the page they have selected, what values are in dropdowns, this kind of code. Because this is a separate concern, the User component above should not store its own state, instead it should expect all data to be available upon render within props.

The clarity of opening a page and seeing the component hierarchy laid out as JSX is too appealing for me personally.

There are many other ways to factor this, I understand concerns with prop drilling and such, but the clarity of being able to read a page and understand its structure at a glance is worth the tradeoffs in my experience.