skip to content

Dependency Injection & Configuration

How the object graph behind the handlers is registered, scoped and fed settings before the first request, and how it shuts down. Asked because most wiring bugs surface here, not in handlers.

on this pageshow

explore

questions

25

In a web framework's component container, how do singleton, per-request and transient lifetimes differ?

level: juniorimportance: must knowfreq 78%

answer

  1. how long one instance is reused
  2. process, request, or every resolution
  3. shared instance needs thread safety
  4. shortest lifetime only for per-request state

basics

~20 s

A lifetime decides how long the container reuses one instance. A singleton lives as long as the process and serves every request; one per-request instance serves a single request; a transient is built fresh at every resolution.

solid answer

~50 s

A container hands out instances according to a declared lifetime. A **singleton** is built once per process and shared by every request, so it must be stateless or internally thread-safe. A **per-request** instance is built once per inbound request and shared by everything resolved while that request is handled, which is what makes it a safe carrier for that request's own state, and it is released when the request ends. A **transient** is built at every resolution, so two collaborators asking for the same type get two objects and nothing is shared. Most frameworks default to the process-wide lifetime for stateless collaborators such as data access, outbound clients and handlers, because building that graph once avoids rebuilding it per request. Reach for a shorter lifetime only when the object genuinely holds one request's state or owns a resource that must be released with it.

go deeper

for a junior

Be able to name the three lifetimes and say what each means in one sentence. The part interviewers listen for is that a process-wide instance is shared by requests running at the same time.

for a middle

Explain the mechanism: the container keeps an instance map per scope, so a lookup hits the process-wide map, the current request's map, or builds new every time. Say what each choice costs.

for a senior

Show that you pick a lifetime from the state an object holds and the resources it owns, and that you treat a mutable field on a shared component as a production defect waiting for concurrency.

for a principal

Frame lifetime as policy: what the codebase defaults to, how the graph is validated at startup, and what a deep per-request graph costs in allocation at peak traffic.

## What a lifetime actually is The **container** is the part of a server-side web framework that builds the object graph behind the handlers: it knows how to construct each component and how to supply that component's own dependencies. Every registration answers two separate questions — *how do I build this?* and *how long do I reuse what I built?* The second answer is the component's **lifetime** (also called its **scope**). Mechanically, a container keeps an **instance map per scope**. When something asks for a component, the container looks in the map belonging to that component's scope. A hit returns the existing instance; a miss builds one, stores it and returns it. Different lifetimes are simply different maps with different opening and closing rules. ## The three lifetimes most frameworks offer | Lifetime | One instance per | Shared by | Released when | Typical use | |---|---|---|---|---| | Singleton | process | every request and every collaborator | shutdown | stateless collaborators, outbound clients, pools, parsed configuration | | Per-request | inbound request | everything resolved while that request is handled | the request ends | request identity, a unit of work, per-request resources | | Transient | resolution | nothing — no sharing at all | with the scope that created it, if the container tracks it | cheap stateful helpers, builders | Frameworks use different words for these — application-wide, container-wide or root for the first; request, scoped or per-call for the second; transient, prototype or per-resolution for the third — but the three behaviours are recognisably the same everywhere, and interviewers ask about the behaviours. ## Singleton: one instance for the process - It is created once, either eagerly during startup or lazily on first use, and then kept for the life of the process. - Concurrent requests genuinely touch the *same object*, so any mutable field is shared mutable state. A singleton is safe when it holds configuration, pooled resources and other collaborators, and unsafe the moment it holds one caller's data. - It is the right home for anything expensive to build: an outbound client with its connection pool, a compiled route table, parsed settings. - A singleton may only depend on components that live **at least as long as it does**. Holding a shorter-lived one freezes that instance for the life of the process, which is the classic lifetime bug. ## Per-request: one instance for one request - The scope opens when the framework accepts an inbound request and closes when that request is finished. Everything resolved inside that window receives the **same** instance. - That shared identity is the point: two collaborators that both need "the unit of work for this request" get the same one without passing it through every signature. - Release is deterministic — when the scope closes the container runs the release hooks of what it created there, so a borrowed connection or buffer goes back. - The cost is construction per request. A deep per-request graph is rebuilt on every single call, which shows up in allocation and tail latency at high request rates. ## Transient: one instance per resolution - There is no reuse and no identity guarantee. Ask twice, get two objects, even inside one request. - It is the cheapest lifetime to reason about, because nothing is shared and nothing can leak between callers. - It is *not* automatically tied to a request. A transient resolved during a request is released with that request only if the container tracks disposables in the scope that created them; some containers track them in the longest-lived scope instead, which quietly accumulates objects that were meant to be short-lived. ## How to choose 1. **Ask what state the object holds.** No per-caller state means the process-wide lifetime, which is the default in most frameworks for good reason. 2. **Ask what it owns.** If it holds a resource whose release must happen when the request finishes, it belongs in the request scope so the container can release it there. 3. **Otherwise prefer the longest lifetime that is correct.** Shorter lifetimes cost allocation and restrict who may hold the component. ## What each choice costs you - Singleton: permanent thread-safety discipline, and hidden shared state is a production bug rather than a test failure. - Per-request: a rebuilt object graph per request, plus the constraint that longer-lived components cannot hold these instances directly. - Transient: no deduplication, so a component resolved five times in one request does five constructions and any state it accumulates is invisible to the other four. The rule that ties all three together is one sentence worth memorising: **a component may hold only components whose lifetime is at least as long as its own.** Everything else about lifetimes follows from it.

  • Why do most frameworks make the process-wide lifetime the default rather than per-request?
    Because most collaborators are stateless: a router, a data-access component or an outbound client holds configuration and pooled resources, not caller data. Building that graph once avoids wiring it on every request, which matters at high request rates. Per-request is the exception you opt into when an object genuinely carries one request's state or owns a resource released with it.
  • If both are short-lived, what is the practical difference between transient and per-request?
    Per-request gives a shared identity: everything resolved during that request receives the same instance, so collaborators see each other's state and the container releases it once at scope close. Transient guarantees the opposite — two resolutions produce two objects — and it is released with the surrounding scope only if the container tracks disposables there.

saying these in an interview costs you the question

  • Thinks a container singleton is the same thing as a hand-written global static instance
  • Says transient is always safest, so everything should be registered transient
  • Believes the container builds one singleton instance per thread or per core
  • Assumes a transient object is always released when the request that built it ends
  • Keeps mutable per-request state in a shared component and expects the container to isolate it
open as a page

What does a dependency-injection container do in a server-side web framework, and how does a request handler get its collaborators?

level: juniorimportance: must knowfreq 72%

basics

~20 s

A dependency-injection container is a registry of construction recipes: you register how each service is built, then ask for one and it builds the whole graph behind it. Handlers declare collaborators as constructor parameters and the container supplies them.

open as a page

In a server-side web framework, what does installing a plugin or module actually do to the application?

level: juniorimportance: must knowfreq 60%

basics

~20 s

Installing a plugin runs its registration code against the application at startup: it adds components to the object graph, attaches request-pipeline hooks, and contributes overridable defaults. It is wiring executed once, not a per-request call.

open as a page

In a server-side web framework, what happens in what order between process start and the first accepted request?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Settings are read and validated first, then the object graph is built, then routes and middleware are registered, and only then is the listening socket bound. Binding last keeps traffic out until the application can serve it.

open as a page

In a framework's component container, why does a singleton holding a per-request component fail only once requests overlap?

level: middleimportance: must knowfreq 66%

basics

~20 s

The singleton is built once and keeps whatever it was given, so a per-request dependency is captured from the first request and reused forever. Later requests then read the first request's state, which only shows up when requests overlap.

open as a page

Why do many web frameworks build or validate the whole object graph at startup instead of resolving dependencies lazily on first use?

level: middleimportance: must knowfreq 58%

basics

~20 s

Because it moves wiring failures from run time to boot time. A missing, ambiguous or cyclic binding then stops the process while no traffic depends on it, instead of breaking whichever request first walks that branch of the graph.

open as a page

In a web framework, how does auto-configuration decide to register a default component you never wired yourself?

level: middleimportance: must knowfreq 64%

basics

~20 s

Auto-configuration evaluates conditions while the application boots: is the feature's library present, is a setting switched on, and is the role still unregistered? When the conditions hold and your own registration is absent, the default is wired.

open as a page

In a server-side web framework, configuration arrives from files, environment variables and command-line arguments - which value wins, and why?

level: middleimportance: must knowfreq 72%

basics

~20 s

Frameworks merge configuration from ordered layers - built-in defaults, packaged files, environment-specific files, environment variables, command-line arguments - and the more deployment-specific layer overrides the earlier one key by key, so the most specific source supplying a key wins.

open as a page

In a web framework, what does binding configuration into a typed settings object give you, and what should be validated at startup?

level: middleimportance: must knowfreq 63%

basics

~20 s

Binding converts merged configuration text into a typed settings object at boot - numbers, durations, enums, nested groups - and checks presence, parseability, ranges and cross-field rules there, so a bad value stops startup rather than a later request.

open as a page

What is a startup hook in a web framework, and what belongs in an eager check that runs there?

level: middleimportance: must knowfreq 58%

basics

~20 s

A startup hook is a callback the framework runs after wiring and before it accepts traffic. Eager checks belong there - validate settings, resolve critical components, verify required resources - so a broken process fails at boot, not at the first request.

open as a page

A running web service is asked to stop while requests are in flight — how should its shutdown proceed?

level: seniorimportance: must knowfreq 64%

basics

~20 s

Flip readiness to false first and keep serving for a moment so routers stop sending work; then stop accepting new connections; then let in-flight requests finish under a deadline; then release resources in reverse startup order and exit.

open as a page

What is a configuration profile in a web framework, and how does it change which settings a service loads at startup?

level: juniorimportance: should knowfreq 58%

basics

~20 s

A profile is a named set of configuration overrides, one per environment, selected at startup by an environment variable or launch argument; activating it layers that profile's values over the base settings without changing the build.

open as a page

When wiring a web framework's container, when do you register a dependency by type, by name, or with a factory?

level: middleimportance: should knowfreq 52%

basics

~20 s

Register by type when one implementation satisfies an abstraction, so the parameter type is the request. Add a name when several instances of one type mean different things. Use a factory when construction needs non-service values or a wiring-time choice.

open as a page

What should a web service's readiness signal report between the socket bind and the end of startup warm-up?

level: middleimportance: should knowfreq 46%

basics

~20 s

Not ready. An open port only means the process is listening. Readiness should flip true at the very end of startup, once warm-up and eager checks have finished, so nothing routes traffic to an instance that cannot serve it well.

open as a page

When a request ends, what must a framework's container do with the components it created in that request's scope?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Close the scope: run each created instance's release hook, normally in reverse construction order, hand pooled resources back, and drop the scope's instance map. Skipping it leaks connections, which surfaces as pool exhaustion under load rather than as an error.

open as a page

In a framework's component container, how can a singleton reach the per-request component of the request in flight?

level: seniorimportance: should knowfreq 54%

basics

~20 s

Resolve late instead of capturing: the handler passes the value in as a method parameter, or the singleton holds a factory it calls per use, or a scope proxy that forwards every call to the active request's instance.

open as a page

Your container reports a circular dependency between two components at startup — what does that cycle mean, and how do you break it properly?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Each component demands the other fully built, so no construction order exists. The fixes change the graph: extract the shared responsibility into a third component, reverse the edge that is only a notification, or merge two classes that are one.

open as a page

Two installed extensions each register a component for the same role — how do you make the outcome deterministic?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Decide the winner explicitly instead of inheriting it from discovery order: declare precedence or a relative order, or register the intended component yourself and exclude the other. Then assert the resolved component in a startup test so an upgrade cannot reshuffle it.

open as a page

A web framework auto-configured a default nobody wired — how do you find where it came from and override it?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Find the source first: read the startup report of which units matched and why, and list the effective registration for that role. Then override at the narrowest seam, and pin it with a test asserting the winning component.

open as a page

A deployed service is using a configuration value nobody put in its configuration file - how do you find which source supplied it?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Stop guessing and enumerate: get the service to print its effective configuration with the origin of each key, then reconstruct the source order, inspect the running process's environment, confirm which profile resolved, and check the key name survived namespace translation.

open as a page

How would you set a default component lifetime across a large service, and what should push a component off that default?

level: principalimportance: should knowfreq 38%

basics

~20 s

Default to process-wide components holding no caller state, and move one to the request scope only when it carries a single request's state or owns a resource released with that request. Enforce it with lifetime validation at startup.

open as a page

When is a dependency-injection container worth its cost over hand-written wiring, and how do you keep a large container-wired graph understandable?

level: principalimportance: should knowfreq 38%

basics

~20 s

A container pays off when the graph is large and churns, or the framework already resolves handlers through one; a small stable graph rarely repays it. Readability rests on one composition root, injected not fetched dependencies, and automated graph checks.

open as a page

Across a platform of many long-lived services, how much wiring would you leave to conventions and auto-configuration versus explicit declarations?

level: principalimportance: should knowfreq 36%

basics

~20 s

Split by consequence: let conventions own the boring, uniform wiring every service wants, and declare explicitly anything user-visible or security-relevant. Conventions buy consistency and a single upgrade point; they cost traceability and make the dependency graph part of your behaviour contract.

open as a page

Across many services and environments, how do you decide which configuration layer owns each setting, and stop override layers from sprawling?

level: principalimportance: should knowfreq 42%

basics

~20 s

Classify each setting by how it varies - never, per environment, per instance, per run - and by sensitivity, then place it in the lowest layer that can express that variation. Cap layer count and share one source order across services.

open as a page

When a whole fleet must restart during a dependency outage, how should its startup sequence and readiness be designed?

level: principalimportance: should knowfreq 38%

basics

~20 s

Design the boot path to complete without its dependencies: verify settings and wiring eagerly, but retry reachability in the background while reporting not-ready. A service that exits when a dependency is down cannot rejoin on its own.

open as a page