Managing Deep Scope Hierarchies in Large Codebases
Article Summary
Uber rewrote their Android rider app in 2016 and made an architectural choice that most Android teams avoid: deeply nested dependency injection scopes.
This article from Uber Engineering explains why their old two-layer scope architecture couldn't scale and how they built a deep scope hierarchy to support hundreds of cities with varying features. They ultimately created RIBs, their own architectural framework, when existing options fell short.
Key Takeaways
- Old app's two-layer scope caused coupling, stale state, and hard-to-test code
- Deep scope hierarchies map controllers 1:1 with scopes, eliminating shared sibling data
- Evaluated MVC, VIPER, Flow, Conductor, and Scoop before building RIBs framework
- RIBs auto-generates scope boilerplate and decouples scopes from views
- Pattern enables static analysis for memory leaks and performance optimizations
Deep scope hierarchies solved Uber's scaling problems by eliminating coupling between features and removing stale state through short-lived, well-encapsulated scopes.
About This Article
Uber's old rider app packed multiple screens into a single LoggedInActivity. This forced engineers to write large classes with fragile state logic, managing dozens of controllers and utility classes across hundreds of cities.
Brian Attwell's team added intermediate scope layers like PreRequest scope where needed. This let the new app's controller hierarchy match its scope hierarchy one-to-one, without forcing sibling scopes to share objects.
Engineers could now build features independently in Home, Product Selection, Airport, and Location Refinement without worrying about breaking existing features. The state combinatorics problem that came with the two-level architecture went away.