DEV Community

Cover image for Dependency Injection for Observability
Samson Tanimawo
Samson Tanimawo

Posted on

Dependency Injection for Observability

Want your code to be easy to observe? Use dependency injection for observability concerns. Sounds dry. Hear me out.

The problem

Your code calls log.info(...) directly. In tests, you can't verify what was logged. In prod, if you want to change the logger, you're grepping the codebase. If you want to add tracing, you're editing every call site.

Same for metrics. Same for tracing. Same for error reporting.

The fix

Pass the observer in. Functions take a logger, metrics, or tracer as an argument (or constructor dependency). The function doesn't know what's behind it.

func HandleOrder(order Order, deps Deps) error {
    deps.Metrics.Inc("order.received")
    deps.Logger.Info("processing order", "id", order.ID)
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Now:

  • In tests, pass in a mock that captures all calls
  • In prod, pass in the real logger
  • Changing backends (Datadog → Prometheus) is one wire-up change
  • Adding tracing is one new field in Deps

Why most codebases don't do this

It feels verbose. 'Why do I have to thread a logger through every function?' Engineers hate boilerplate.

The alternative is a global singleton. Easy to use, impossible to test cleanly, nightmare to refactor.

The boilerplate is worth it. Especially for observability, where you will want to swap implementations later.

The subtle win

Dependency-injected observability forces you to think about what you're observing. When you have to explicitly pass the logger, you notice that a function is calling it 8 times. Is that too much? Is the logging doing real work? Would one structured log at the end of the function be better?

Functions with injected dependencies tend to have better observability because the developer had to look at it.

The starting point

You don't need to refactor everything at once. Pick one critical path — the checkout flow, the auth path. Refactor just that path to use dependency injection for observability. See if tests and debugging get easier.

If yes, expand. If no, you found something else is the bottleneck.

The bigger idea

Observability is code. Treat it with the same architectural discipline you treat the rest of your codebase. Dependency injection is one tool. There are others (context objects, middleware, decorators). Pick one, apply it consistently, and your future-you will be able to observe your code in ways that are impossible with ad-hoc log.info calls everywhere.


Written by Dr. Samson Tanimawo
BSc · MSc · MBA · PhD
Founder & CEO, Nova AI Ops. https://novaaiops.com

Top comments (0)