Skip to main content
Uber logo

Schedule rides in advance

Reserve a rideReserve a ride

Schedule rides in advance

Reserve a rideReserve a ride
Mobile

How Uber’s New Driver App Overcomes Network Lag

28 November 2018 / Global

This article is the third in a series covering how Uber’s mobile engineering team developed the newest version of our driver app, codenamed Carbon, a core component of our ridesharing business. Among other new features, the app lets our population of over three million driver-partners find fares, get directions, and track their earnings. We began designing the new app in conjunction with feedback from our driver-partners in 2017, and began rolling it out for production in September 2018.

The competition between urban architecture and wireless data technology means lapses in coverage—dark spots in cities where our phones won’t work. Driving through urban landscapes means finding more of these dark spots, leading to frequent changes in network quality and levels of congestion. These lapses in coverage particularly affect Uber’s driver-partners as they attempt to pick up or drop off riders.

The pain points here can be demonstrated best by an example. Suppose a driver finishes a trip at a crowded airport in Bangalore. The rider wants to pay with cash, and the driver needs to complete the trip in the app to see the final fare. Pulling up to the curb at the airport, the driver’s phone can’t connect to the Internet. The rider is rushed to make their flight, but the lack of a connection means the driver can’t complete the trip in the app and get the final cost. The driver might drive further down the terminal, taking extra time, potentially extending the trip, and causing frustration for both rider and driver.

To deal with lapses in network coverage and prevent these types of scenarios from occurring, we came up with Optimistic Mode. This new feature for our driver app lets the app work offline so that a driver can end a trip even without a connection and retrieve the last price estimate received from the server. Optimistic Mode allows the app to work regardless of network conditions, leading to more positive trip experiences for rider and driver alike.

 

Optimistic Mode components

We supported some offline capability in the previous driver app by collecting failed requests and batching them to the server to be consolidated once connectivity was regained. While this feature helped prevent some errors from being displayed, it wasn’t able to intelligently update the state of the application, stack multiple actions on top of each other, and persist state across sessions. We developed the components described below for our new driver app to deal with these issues.

Optimistic requests

Any component of the driver app capable of operating optimistically begins the flow by submitting an optimistic request. An optimistic request has the ability to serialize and deserialize to disk, very similar to a regular network request, and every optimistic request is paired with an optimistic transform.

Optimistic transforms

The main component that allows Optimistic Mode to work are called transforms, in other words,  operations that transform the current state of an object to an optimistic state, i.e., the expected state to be returned by the network. Transforms can also be stacked, applying their changes in order as an object passes through each transform. To understand transforms with a simple example, let’s imagine a class “Counter” which has a property “count.” We can then implement a transform which increments the count property of the Counter object.

Carbon: Optimistic Mode article figure 1
Figure 1: In this simple example, the Increment Transform increases the count property by one as the Counter object passes through.

 

Transforms can be as simple or complex as needed for our optimistic operations. Each optimistic request has a transform associated with it. The transform outputs an optimistic state that matches the eventual response from the optimistic request. This way, the user will not notice any change in the app when the response comes back from the network, providing a smooth transition.

When an optimistic request is submitted to the client, the transform associated with the request is applied immediately to move the app into an optimistic state, making it appear that the request has completed. The optimistic state outputted from the transform will be maintained until a response from the server is received with the actual state, syncing app and server.

Carbon: Optimistic Mode article figure 2a
Figure 2a: Normal counter request fails to upload to a server.
Carbon: Optimistic Mode article figure 2b
Figure 2b: A transform is used in Optimistic Mode to update the state immediately so a workflow can be completed even without network, with the assumption that it will sync with the server in the future.

 

Optimistic stream

We use RX streams as the message bus for data to be passed through the app. Every feature in the app reacts to the state changes that are published on the datastream. This mechanism enables us to use the same stream to easily apply optimistic transforms to the latest state of the object. To obtain the optimistic state, we combine the last known state of the data on the stream with the available transforms for the data. The data has each transform applied to it before being published back on the stream and consumed by the feature. The feature then simply reacts to the optimistic state of the data.

Dependent requests

There are also requests that are dependent on optimistic requests completing. For example, it wouldn’t make sense to send a request to end a trip that the backend doesn’t even know has started. Such dependent requests will be queued for a period of time while we wait for the optimistic requests to complete. If this period is too long, we fail the request, notifying the user with a network error message.

 

Design challenges

We faced several challenges in this design. We wanted to support stacking optimistic requests, allowing for multiple steps to be completed without a network connection. Due to being out of sync with the server we also needed to handle cases where we incorrectly moved into an optimistic state and must revert to a previous state. Ensuring that we show the driver the most accurate state reliably is something that took several iterations and will continue to be optimized as we move forward.

Rebasing transforms

With Optimistic Mode enabled, the application may receive other network data before the optimistic request has been able to complete.

Carbon: Optimistic Mode article figure 3
Figure 3: In this scenario, we apply our optimistic transform on top of the latest state that we receive from the server.

 

For example, let’s take the counter example we used above. The app increments the count using the transforms to give a final value of 2. However, this count has not yet synced with the server. During this period of time, other network responses received may have a stale value of 1. Optimistic Mode uses the transforms to update the stale state and maintain the optimistic state. This ensures that the app does not revert back and forth for the user, between two states, avoiding a confusing experience.

Surviving application restarts

All optimistic requests along with the last known optimistic state are saved to disk so they persist across application restarts. Consider a scenario where there are a few requests queued up to be synced with the server, but the user closes the app. Upon re-launch the optimistic requests and last known optimistic state are loaded from persistence. This allows the users to be in the same state when they re-launch the app. The optimistic requests are queued up to sync with the server.

Surfacing errors

A particular issue we come across with this new feature is how it surfaces errors. Optimistic Mode was designed for requests that should only fail due to back-end outages, and should have predictable responses that can be mocked. However, in practice errors will arise. Because we move the user through the app workflows optimistically, an error can be a very jarring experience. Firstly, the app state rolls back to the pre-optimistic state, leaving the user in an unexpected state where the next action may not be obvious. Secondly, in order to surface errors we need the previous state to receive error messages, even though it may have already gone out of scope. To handle this, in the driver app we create a global error handling framework, which we call internally the Alert Framework.

There will always be the rare case when a server returns an error to a request. For commonly occurring error, such as when trips are too short, we implemented checks on the mobile clients to handle them better.

Saving time

For drivers, we’ve seen great savings in time spent starting and ending trips, which are the first two operations that utilize Optimistic Mode. We often see that a trip was able to progress several minutes before a network operation was actually able to complete. As of November 2018, we have observed that the average time saved per optimistic operation is about 13.5 seconds. Even at this early stage in the new driver app’s life we are totaling over a year’s worth of continuous driver time saved in aggregate each and every day.

 

The future of Optimistic Mode

The ability to progress user state without a network connection has shown to be useful for other flows in Ubers apps as well. Launched as a way to speed up starting and ending trips, it has also been integrated into delivery-partner features for Uber Eats, allowing for quicker drop-offs when cash is used as payment. It can also be leveraged for features that need to react quickly but can sync with the server later, such as rating a rider or driver, marking inbox messages as read, or collecting signatures for deliveries.

 

Index of articles in Uber driver app series

 

Interested in developing mobile applications used by millions of people every day? Consider joining our team as an Android or iOS developer!

Carbon: Optimistic Mode article team photo
Uber engineers who developed Optimistic Mode for the new driver app, from left to right: Chris McGrath, Flynn Howling, Chris Francis, Jatin Lodhia, and Yohan Hartanto.
Chris Francis

Chris Francis

Chris Francis is a software engineer at Uber. He currently works on optimizing network performance for Uber’s Android apps. He previously worked in a voice messaging startup in San Francisco.

Chris McGrath

Chris McGrath

Chris McGrath worked as a software engineer at Uber.

Flynn Howling

Flynn Howling

Flynn Howling is a software engineer at Uber. He currently works on improving the network performance of Uber’s iOS apps, and digging through data to find insights. Previously he worked at a fast growing startup in Canada where he helped write popular iOS apps.

Fletcher

Fletcher

Yohan Hartanto, a software engineer at Uber, currently works to scale the development of the driver app to be reliable and highly performant. In the past, his experience includes building Android SDKs for Fabric, and developing an Android music app for Amazon.

Posted by Chris Francis, Chris McGrath, Flynn Howling, chanceymathewsuber, Fletcher

Category: