Ognjen Regoje bio photo

Ognjen Regoje

I make things that run on the web (mostly).
More /ABOUT me.

me@ognjen.io Twitter LinkedIn Instagram Github

SPAs and PWAs should duplicate the store and the routes

The DRY principle says that a change in any element of a system does not require a change in other logically unrelated elements. In order to offer a better user experience we need to stop considering the client-side and server-side store and routes as logically related.

Duplicate (some) of the store

Data is stored only on the server.

But the client can store data too

The browser has several ways that can store data, at least temporarily. Cookies, localStorage and indexedDB are the most common ways.

localStorage in particular is very well supported and very easy to work with.

It can store forms that have not been submitted. If the user returns to the page the information can be pre-populated. From experience on Supplybunny I know that this has a measurable effect on improving checkout rates.

Another benefit of storing data locally is to offer core functionality without having the user register. Asking for the users personal information before showing what value you provide can be a big leap.

In Kocokapp, my fashion discovery side project, for instance, all choices are stored in localStorage. Registration isn’t required. It’s in my interest that the users’ favorites are available to them even if they aren’t registered. If I demonstrate value users will be eager to register. When they do it is trivial to copy their choices to the server.

We also seem to have forgotten that the server can access data

So, the server responses are DRY and don’t include the data that will be needed immediately. In fact, responses often don’t include any date.

The user loads the page and sits through a loading spinner. Immediately after the page loads the client issues additional requests for the data. The user then sits through a skeleton screen. 🤷

Furthermore, the requests issued neatly correspond to the backend database tables. This is very convenient for the development team. But it’s worse for the users.

The first step to improving this would be to at least combine several requests into one. For example, instead of calling /user.json, /orders.json and /promotions.json make /home.json return all three.

The proper solution though is for the server to return all the data immediately required on page load.

This is straight-forward and easy to implement using the __INITIAL_STATE__ pattern that’s been around for a while. The server loads the data and returns it in JSON in __INITIAL_STATE__ variable. When the front-end initializes, it picks up everything that’s in __INITIAL_STATE__. The user sits through the spinner but doesn’t have to sit through the skeleton screen.

There are a couple of challenges with implementing this approach:

  • Not wanting to duplicate the database access Having the front-end issue requests to the API that already hads database access is very neat and DRY. This architecture can be preseved by having the web server instead of the client request the data from the API. In fact it would be an improvement because server to server response times are often quicker then user to server. Not only would requests be eliminated but they’d be quicker as well.
  • Migrating to this approach would take time True, but the easing circumstance here is that improving a single page often means that you’re nearly done. And I do think the payoff is worth it.

Duplicate (all) the routes

Similarly to the store, in order to be DRY we often only do routes either on the server or on the client.

A lot of single-page applications in particular do not have routes. When the users refresh the page they’re dropped into the root view.

This can be quite disruptive for the user if they were a few levels deep. It also means that sharing links, one of the best features of the web, is broken.

For web application to have functional links routes should exist on the server and client.

The server should be aware of the client’s routes so that it knows what data to load.

The client should have it’s own routes to keep track of the user’s location and to preserve linking.

Having detailed routes is particularly important for PWAs. Without them, their functionality can be severerly limited. In particular, since they can be suspended at any point, not having sufficiently detailed routes makes it impossible to restart them correctly.

Having proper routes in PWAs also allows users to share very deep links. This is functionality that’s rarely, if ever, implemented in mobile apps.

And mobile apps should listen too

While my advice/opinion here is for web applications I think it partly applies for mobile applications as well.

They should combine multiple requests.

They should make use of local storage mechanisms to provide functionality without logging in.

They should implement good routing and deep linking.

So, in conclusion

In our search for DRY we do some things that are convenient for us but make the experience worse. Not copying the store and the routes make the experience worse.

We must be aware that while having good architecture is important, it is in the service of building something that helps users accomplish their goals. That means prioritizing the user rather then the architecture.

To that end, we should:

  1. Return data that’s immediately needed on page load
  2. Reduce the number of requests the clients make
  3. Make use of local storage mechanisms to provide core functionality
  4. Preserve the ability to link to pages

#architecture #front-end #kocok #product #ux