Thep Urai - Fotolia
There was a time when desktop productivity applications just worked incredibly fast. The advent of cloud applications has made it far easier to roll out application updates, but user experience has suffered, in large part, due to a paradigm requiring constant network connectivity.
At the O'Reilly Fluent Conference in San Jose, Calif., Islam Sharabash, founding engineer at San Francisco-based Superhuman, elaborated on their experience building a blazingly fast email client.
The key insight lies in setting up the application's user interface to work against a locally stored cache for all user operations. This approach means the app can respond to each request in under 100 milliseconds. But developers also need to architect the communications between front end, cache and back end thoughtfully to prevent data loss or corruption.
Sharabash said an ideal architecture to enable cloud productivity applications to provide a good experience regardless of connectivity status requires:
- A way to load code offline;
- A way to store up to gigabytes of data;
- A way to detect connection status; and
- An architecture that makes it easy to build offline features.
The qualities of this architecture should include speed, robustness and simplicity. Ideally, developers should only have to implement features once that can work both online and off.
Use a modifier architecture, rather than optimistic updates
Many popular cloud apps do have some support for leveraging browser caching today. But Sharabash walked through an example of how popular cloud applications, such as Trello and Google Calendar, can lose data. He pointed out that these apps use optimistic updates that save changes on the UI before they have hit the back end when the user is offline. When they go to connect later, the changes are lost.
"Trello and Calendar are tremendous products," Sharabash said. "But I just wasted minutes and lost critical data. When doing mission-critical work, this is unacceptable."
Sharabash said a much better approach is what he called a modifier architecture that includes modifiers, queues and workers. Modifiers change the data and provide optimistic updates, and roll back when an update can be executed. The queue sorts the order of requests and prevents race conditions. Web service workers use an emerging feature in modern browsers to save the data changes to the back end.
The modifier layer communicates synchronously with front end to reflect changes, and it communicates asynchronously with the back end through queues and the service workers. For example, if the user changes a label on an email thread, this is reflected quickly in the UI, but the update is sent to the back end when possible. If they decide to remove the label, this is also queued up to occur subsequently. Items in a long queue can be combined to reduce network usage. So, if someone decided to add and remove a label, the queue could combine these to just cancel them out.
Work with queues and a cached source of truth
If the network fails in the middle of the operation, there needs to be a mechanism in place so the modifier can execute the change later. To address this need, the architecture also needs to include a cached source of truth in the browsers to handle cases where synchronization with the back end fails. Modifiers also need to be composable so they can be stacked to represent multiple operations.
The combination of modifiers and queues makes it possible to create fully interactive apps. The last part involves saving changes to the back end performed by the service worker. The workers take modifiers from the queue and apply them against the back-end application running in the cloud. Modifiers are only removed from the queue once they have been applied to the back end. After the change has been confirmed, the client can then update the cached source of truth.
Some types of errors occur because the client is not connected. But, in other cases, there might be other problems preventing synchronization. In these cases, the app needs to retry a set number of times before informing the user the change could not be made. Sharabash said they have experimented with allowing up to 100 retries, but settled on five.
Dealing with edge cases
This kind of architecture also needs to address a variety of edge cases. In the simplest one, a user might be working across two tabs on a browser on the same computer. In a more complex case, they might be accessing the same cloud productivity applications from different browsers running on different computers.
With multiple tabs, it is possible to have independent sets of service workers issuing conflicting updates against the back end. "The answer is to guarantee a single tuned worker," Sharabash said. Superhuman engineers created a Chrome extension that does this across all tabs. He said other options include electing a master tab to drive all service worker requests or using local sourced events to communicate between tabs.
Other problems could emerge when someone tries to work from different computers. For example, if someone applies a star to an email on one computer and removes it on the other while offline, the cloud app needs to understand their intent. This could be an issue if second computer connects to the cloud first, since the changes would be applied to the back end before the first computer synchronizes.
The one downside to Superhuman's current architecture is it only works on Chrome today. This allows them to take advantage of subtleties in the browsers implementation of caching features and service workers that are implemented differently on other browsers. This kind of approach might be fine for productivity applications in which an enterprise has standardized on one browser. But, it also limits the potential user base.
Making productivity apps work in your enterprise
Are Google productivity apps helpful?
Working with mobile productivity applications