DI Isn't About Saving `new` — It's About Who Owns the Dependency Graph
Without centralized dependency management, a growing Android codebase accumulates creation logic in every layer, so a constructor change or a new environment-specific implementation forces edits across dozens of files. DI moves that assembly work into a single configuration surface, which is the difference between a one-line swap and a multi-day refactor.
Direct object creation works fine in small Android projects. A ViewModel instantiating a Repository is simple and clear. The trouble starts when business classes begin assembling their own dependency chains — a LoginViewModel that builds Retrofit, configures a base URL, and wires up a UserApi has stopped caring about login logic and started caring about infrastructure. Constructor changes ripple through every creation site, and swapping implementations means hunting down every `new` call. DI frameworks like Hilt or Koin invert that control: a class declares what it needs, and the framework resolves the full object graph, deciding which implementation to supply and managing its lifecycle. The shift is from business code owning creation decisions to architecture owning them, which keeps responsibilities clean as the dependency graph grows from a handful of nodes to hundreds.
The argument frames DI as an organizational boundary rather than a convenience — it's about separating the question 'what do I need?' from 'how is it built?', which is a more durable mental model than 'DI saves typing.'
Framing the problem as 'who owns the dependency graph' rather than 'how to create objects' explains why teams resist DI early and adopt it late: the pain is invisible until the graph is large enough that a single change breaks multiple files.
The post implicitly argues that DI's value scales non-linearly with project size — negligible at 5 classes, indispensable at 50, and a hard requirement at 500 — which matches the experience of teams that defer DI adoption until a refactor forces it.