There are 2 ways to think about the web application you're working on.
In truth there are way more than 2, but this article is about 2 particular ways and how they are different.
The difference is subtle and a bit nefarious because the idea is more philosophical than technical, but can have damaging effects when 2 or more members of the same team view their application differently.
Personally, I find it very frustrating when articles drag on like some sort of mystery novel, yammering about this and that until they finally get to the “big reveal” of what the article is actually about, and in my first draft of this article I found that’s exactly what I did, so with the power of editing, allow me to start off by describing the 2 ways to think about your web application:
In this mental model, the application is generally thought of as “on the server” (regardless of what architecture that server is built on) and the web portion of that application is the User Interface (UI) or “view” of that application.
I would say this is often the “traditional” perspective taken by developers, especially those who did not start their careers in front-end development, though with all things of course there are exceptions. Storage is most often within databases of one kind or another, with decisions and business logic in server side code, sometimes exposing API interfaces such as RESTful APIs or GraphQL and other times web pages by serving HTML from URL endpoints.
In the distributed mental model, the client-side is the application. The RESTful and GraphQL APIs serve to support the application with services such as storage, aggregation and heavy calculation, but decisions, state management and UI are handled in the app, on the users device.
This is not to be confused with the narrow definition of distributed apps (dApps) running on top of a blockchain, smart contracts or peer-to-peer (P2P) networks, this is still just referring to web applications even if they have centralized authority and storage, but distributed in the sense that the application is intended to run on many machines independently, with both the benefits and detriments that entails.
This is often how native mobile developers model their thinking, and due to the rise of Single Page Apps (SPAs) on the web, has been a common perspective in web developers for many years now as well.
As mentioned before, this difference is subtle and is not based on the technology or framework, but instead it is more like a philosophy or a framework for your mind. It can be so subtle in fact that 2 members of the same team, working on the same codebase, can find themselves viewing the app differently in this regard.
This can lead to issues.
When the mental models of the application on a team are not aligned in this way, the problems often manifest themselves as criticism of “over engineering”, snarky remarks, hostile guffaws about “doing it wrong” and the far more insidious manifestation: assuming your co-worker “just doesn’t really know what they are doing”.
Sometimes, this can lead to wasted time, as developers debate and argue over technology choices. Sometimes it can grow worse as bolder developers change the work of others because "that should run on the server" or some other point of view. And sometimes, just sometimes, it can lead to conflict and the breakdown of trust on a team.
By clearly defining and documenting if your application is using an Interface Model or a Distributed Model, you can circumvent these issues, and it can even add another dimension to evaluate technology choices on, as some tech may feel more or less appropriate depending on your applications model choice.
If you are working on an interface model application, state management libraries such as Redux, NgRx, even MobX and others may seem like overkill, because you're not really working with your application state, you are working with a local cache of the application state.
I think the big feelings of great joy and gratitude that gushed forth across the intertubes when libraries like Recoil, SWR and the wicked-smaht React-Query, hit the mainstream, were directly from the idea that their concepts fit the mental model of an interface model application better.
Why I Stopped Using Redux is a great article, and the author is thinking about applications in the Interface Model and yet, immediately in the comments I see people responding who are thinking in terms of the Distributed Model. But without the framework or language to differentiate, they assume the author is “wrong” and tend to conflate the breakdown with complexity or scale, which seems to baffle other commenters who are thinking in terms of the Interface Model.
In the article My State Management Mistake, Kent C Dodds laments confusing "server cache" with UI state (the stuff that disappears when you hit refresh), and though I wholeheartedly agree in the difference, I think the article is very slanted towards the Interface Model and the same problem could be dealt with, also by separating the business domain data and the UI state on the client, if you were building with the Distributed Model in mind.
In a Distributed Model app, that’s not “just the cache” you’re working with, that is your apps current data and state. The GraphQL and RESTful API’s just serve their role of storage, aggregation and a service for heavy calculations, so, what may seem like over-engineering your server cache, may seem perfectly reasonable in the distributed context.
For a long time now, since the rise of SPAs, the Distributed Model was very popular in modern web development. Even when many developers expressed the idea we had strayed too far from traditional web apps “just for some fancy animations and transitions”, there continued to be litany of state management libraries released specifically for handling all your application needs right in the client.
But, the tides are certainly changing now, and with the rise of “island architecture” frameworks, like Remix, Hotwire, and others the pendulum is swinging back towards the Interface Model.
But there is still a place for the distributed model in web applications.
If, for example, your team consists of mostly front-end developers who are comfortable with client side technology, that’s probably reason enough to work in the distributed model. The right tool for the job adage definitely includes the skills your team currently have.
You may also have an app that could make the jump to a
dApp, built on Ethereum or some other distributed system, removed from centralization, and starting your app in the distributed model may better prepare you for that jump.
Maybe one of your app's primary features is offline use, and if a good portion of your app can be used without a network connection, that may be a strong indicator that the distributed model may be more appropriate.
It may also be that your app is simple in its data requirements, and differentiates itself with design and interactions, and treating the app as an interface model application just doesn’t feel right.
And maybe you want to minimize your server costs and security concerns, so you’re leaning full Jamstack, pushing your app to edge CDNs and leveraging cloud services like Auth0 and Fauna to handle the parts you just don’t want to deal with.
Conversely, using any technology that blurs the line between client and server is probably a good sign that the Interface Model is more appropriate for your application:
- If your application implements an RPC (remote procedure call) API
- React’s upcoming Server Side Components
- Tools like Blazor (C#) or Flutter (Dart)
...or similar technologies.
So in closing, my advice is this:
Decide and document which mental model you are designing your app with. Evaluate your tech stack and see if alternatives may seem more appropriate with that decision in mind. Ensure every new developer onboarded to the team is also informed whether your app is built on the interface model or the distributed model. Stop assuming people don’t know what they are doing, and rather, see if you can understand how they are thinking.
That last one is pretty good advice in general.