What Is Dependency Injection? | Vantage

Dependency Injection Dependency injection (DI) is a software design pattern in which an object or function receives the objects it depends on (its dependencies) from an external source rather than creating them internally. Instead of a class instantiating a database connection itself, it receives a database connection as a constructor argument or parameter. The component that provides dependencies (the injector or container) is responsible for creating and wiring the dependency graph. DI frameworks (Spring in Java, Angular in TypeScript, FastAPI in Python) automate this wiring based on type annotations.

Why dependency injection matters

Code that creates its own dependencies is hard to test and hard to change. A service that instantiates its own database client cannot be tested without a real database. Dependency injection makes the dependency explicit and replaceable: in tests, inject a mock database client. For different environments, inject a different configuration. For A/B testing an implementation, inject the appropriate version. The practical effect is that code with well-applied DI is dramatically easier to test, modify, and understand.

How it works

At the simplest level, DI means passing dependencies as function arguments or constructor parameters rather than creating them inside the function. At the framework level, a DI container reads type annotations and automatically creates the dependency graph — when you request a UserService, the container sees it needs a Database and creates one, then injects it. Dependencies are typically registered with a lifecycle: singleton (one instance for the application), scoped (one per request), or transient (new instance each time).

Common mistakes

  • Over-engineering with a DI framework when simple function parameters suffice — DI frameworks add complexity that is only justified in large codebases

  • Injecting everything — not every dependency needs to be injected; simple utility functions and value objects do not benefit from DI

  • Service locator anti-pattern — calling a global registry to retrieve dependencies is not dependency injection; it hides dependencies and is harder to test

  • Constructor over-injection — a constructor with more than five injected dependencies is a signal that the class has too many responsibilities

  • Not using DI for external I/O — the highest-value application of DI is isolating code that calls databases, APIs, and file systems, making it replaceable with fakes in tests

Related terms

How Vantage relates

Vantage-generated tickets for service implementation work can reference architectural patterns like dependency injection as part of the technical context. When a PRD requirement involves a service that needs to be testable and configurable across environments, the generated tickets can explicitly call out DI as the implementation approach, aligning engineering work with the architectural standards captured in the team's ADRs.

Frequently asked questions

Put product concepts into practice

Vantage connects theory to execution. Generate grounded PRDs, track requirements, and ship with confidence.

Free to start. No credit card required.

Related reading