During the last two years, I’ve heard the term Isomorphic Web Apps mentioned in a positive way more and more frequently. Also during this time, I’ve done some thinking myself about the technique. My conclusion is that Isomorphic Web Apps is something that would not add value for me or the typical organisations I work for. Several things have led me to this conclusion.
The reasons why I don’t believe that Isomorphic Web Apps are valuable for me or the organisations I work for are:
- Isomorphic Web Apps can never be Progressive Enhancement for non-trivial apps
- Blocking vs non-blocking data flow
- Time-to-interaction and the Uncanny Valley
- Your best developers are now busy not producing value
I want to start with a definition of Isomorphic Web Apps. An Isomorphic Web App is a web app where the application code has no knowledge of where it’s run. Instead, this knowledge is kept in the infrastructure code and allows the application as a whole to run on both client-side and server-side.
Isomorphic Web Apps can never be Progressive Enhancement for non-trivial apps
For me, Progressive Enhancement is to start with a baseline that is accessible for all possible devices/browsers, from old to current to future. In practice, this means a baseline of server-side rendering. The enhancement step is a business decision on where to enhance the site/app to increase the value. In other words, the enhancement is done in the application code (specific) and *not* in the infrastructure code (general), except for optimisation techniques like pjax or turbolinks.
The way for Isomorphic Web Apps to get Progressive Enhancement is to take this fine-grained in-memory state machine and “translate” it to only use only links and forms. The only cases I can see where this is possible is where you didn’t need full client-side rendering in the first place, but instead could rely on above mentioned optimisation techniques and client-side components for enhancing the experience (i.e. a calendar component for an input field for a date).
A variant of this solution is to not support every client-side state/transition in the server-side state machine. In this case, this should be a business decision that needs to be reflected in the application code, making the application code environment sensitive, which goes against the idea of Isomorphic Web Apps.
The way for Isomorphic Web Apps to get Progressive Enhancement is to take this fine-grained in-memory state machine and “translate” it to only use only links and forms.
Blocking vs non-blocking data flow
The rendering sequence for server-side web and client-side web are different: the server-side blocks and the client-side doesn’t block.
Imagine that we need to do two requests to some services in order to render a view. We can do these requests in parallel.
On the server side, if the second request returns first, it can render that part of the response but needs to block sending those bytes until the first has rendered and returned that part of the response back to the browser.
On the client side, this constraint doesn’t exist: the two responses can be handled and rendered independently.
Also, I think the above represents the simplest of examples. Imagine you have a tree of components, where the root component is smart (see Presentational and Container Components for an explanation of smart/dumb components), followed by a few levels of dumb components, and then at least one leaf component that is smart.
The infrastructure then needs a way to make sure the smart leaf’s context doesn’t get lost, so that we lose control of the blocking/non-blocking execution, depending on server/client mode. One way to solve this problem could be to make the whole program be executed in a free monad, representing the execution as data to later be interpreted. Another solution could be to use some form of visitor pattern and let components declare what data they need (similar to Tutorial: Handcrafting an Isomorphic Redux Application (With Love)). The latter is probably easiest. My point is that the problem of different blocking modes are probably much more complicated that one imagines initially.
An alternative design is to have a rule that says “only the root component can be a smart component”, similar to how you might use the IO Monad in Haskell, keeping the leaves pure and only having side effects at the top level. I think this design is good on the server side but not on the client side: a component on the client side should be able to load its own data in at least some scenarios, for example regarding social media related components. Having a rule that never allow for these scenarios seems very unproductive.
Time-to-interaction and the Uncanny Valley
See this tweet and the responses for more details.
Your best developers are now busy not producing value
Isomorphic Web Apps is an approach that demands a high level of development skill. In many areas (in the western world, at least), it’s difficult to find and recruit highly skilled developers. Choosing to develop Isomorphic Web Apps allocates those developers to do something that I question produces any business value at all. There are better ways to make use of their time.
Finally, I want to stress that your experiences might be different from mine. Maybe there are scenarios when Isomorphic Web Apps are good. But they are not a silver bullet for bridging every gap between server-side web benefits and client-side web benefits, while getting none of the drawbacks of both approaches.
What do you think? Have you considered “going isomorphic”? Have you any thoughts about the challenges and costs? How do you deal with them?