Optimistic UI vs. Objective state, which pattern is better?

One of the core strengths of the Meteor platform are its optimistic updates, that favor a local state vs. a server-side state to achieve much more responsive web UIs, at the cost of potential temporary glitches (a server rejecting an update).

In industrial applications, like control systems that operate machinery in factories, a similar pattern is used to change remote state, called “objective state”. In this pattern a client sends a target state to a server, while a server sends an actual state back to the client. The actual state should eventually converge to the provided target state.

Often a server also sends a target state to the client, representing the target the server is currently converging to, which is useful when there are multiple clients.

An example of this pattern can be found in https://www.corto.io (a data-backend for industrial systems) by using target datatypes (disclaimer: I am a developer for corto):

struct Thermostat::
    temperature: target{int32}

In code:

// Updates 'target when run on client, 'actual' when run on server
corto_int32Update(myThermostat->temperature, 65);

The objective state pattern prevents the temporary glitches (important in industrial systems), while still being able to leverage local (fast) updates. It’s advantages however diminish when the convergence between client & server is very fast.

I’m intrigued by Meteor’s optimistic UI as clearly a lot of thought went into it and seems to work generally well. Though I am curious whether the objective state pattern could have an application in web apps as well. For example:

  • For high-latency connections (bad cellular network)
  • Show more info in a UI about the progress of the server converging
  • Improve UX by preventing “jumping back in time” if the server rejects updates

Any thoughts?

2 Likes

How does this system prevent a jump back in time? What if the target value turns out to be incorrect?

The target isn’t “incorrect” if it correctly expresses the desire of the client. The server can however consider that desire to be unreasonable, and may notify a client that it will never be able to fulfill that request.

That may seem pedantic, but ownership of data is an important property in this pattern. The target is “owned” by the client, while actual is “owned” by the server. Ownership means that nobody can change or question the validity of your data.

The convergence between target and actual also isn’t necessarily binary. On a hot day I may set my AC to 50F, and even as actual will approach target, it is unlikely that it will ever get there. That doesn’t mean my target is wrong, in fact it may be my AC that is broken.

Perhaps a better example for a web UI: Suppose I have a webshop, to which I can add items of a specific quantity. The quantity expresses my desire (target).

The webshop UI could initially prevent me to select more than the actual number of items in stock. However, this number could change when somebody else just made a purchase.

When the actual goes below target, I could provide feedback to the user; not necessarily decreasing the number of items in the cart (the user still desires X items) but perhaps color the number red.

There is no “jumping back in time”. Instead the webshop developer has more information to provide useful feedback in the UI; though at the cost of some additional complexity.