Centralized Dynamic Navigation at Scale: Zero Crashes, Module Independence, Faster Delivery
Article Summary
Aditya Singh from slice engineering solved a problem that haunts every Android team at scale: navigation crashes from circular dependencies. His team hit zero navigation crashes in production with a runtime graph resolution approach.
Large Android apps with 20+ feature modules face a brutal tradeoff: either tightly couple modules through navigation dependencies, or risk IllegalArgumentException crashes from circular graph inclusions. slice engineering's solution treats all navigation as deep links and loads destination graphs only at runtime when needed.
Key Takeaways
- Zero navigation crashes in production since rollout, 100% success rate
- Destinations load lazily at runtime, avoiding circular dependency hell
- New modules onboard by adding one path prefix, no cross-module edits
- Memory and startup stay lean: no pre-inflated unused graphs
- Routes switchable via config without app releases
By dynamically resolving navigation graphs at runtime instead of compile time, slice achieved zero navigation crashes while eliminating cross-module coupling across 20+ feature teams.
About This Article
Slice Engineering had 20+ feature modules that were tightly coupled through navigation dependencies. Each module had to expose its exit navigation interfaces via Dependency Injection, which meant recompiling dependent modules whenever navigation changed.
The team built a NavHandler resolver that loads only the required module's graph at runtime using deep-link path prefixes like /savings/ and /deposits/. This made destination registration idempotent and removed circular dependency loops.
Build times improved because navigation no longer created cross-module compile dependencies. New modules could be added by touching just 1-2 files without editing anything else. Crashlytics showed zero navigation failures and no user-reported issues.