How Uber Engineering Verifies Data at Runtime with the Annotations You Already Use
26 May 2017 / GlobalUber’s hypergrowth forces our developers to engineer stability into our apps using resourceful techniques.
In 2016, for instance, we created and open sourced Runtime Annotation Validation Engine (RAVE), a data model validation framework that uses Java annotation processing to tackle the number one cause of crashes in our Android apps: NullPointerExceptions (NPEs). NPEs are a common issue with languages like Java that do not have nullability built into their type system. By leveraging the annotations you already use, RAVE acts as a shield that protects against crashes or hard-to-spot bugs caused by invalid data.
To commemorate today’s release of RAVE 2, we explore how we eliminated the vast majority of NPEs in our apps using this powerful tool.
NullPointerExceptions in Uber’s Models
In Android apps, NPEs are frequently thrown when null data is accessed in models. Static analysis tools like Infer help catch NPEs at compile time but are not capable of determining whether data received at runtime (i.e., from network or storage) conforms to the set of expectations that are described by the annotations present in models.
One of the largest contributors to NPEs in our Android apps came from making assumptions regarding the data we used in our models. Consider the Rider model object shown below:
This model uses nullness annotations to inform consumers whether or not return types can be returned null. While these annotations are capable of warning developers working in an integrated development environment (IDE) when null types are unchecked or incorrectly used, they do not provide any safety at runtime. For example, when an app receives data and uses it to inflate model objects, there is no enforcement requiring that the data conforms to the annotations present in the model.
This scenario frequently occurred when deserializing objects from the network while using Gson. Since Gson does not check that the model objects it creates respect nullness annotations, an NPE can occur when you try to access annotated @NonNull