Live data from Hacker News

The problem with dependency injection frameworks

jamesshore.com

51–60 of 92 posts

Re: The problem with dependency injection frameworks

#51

Earlier quoted context omitted.

Yeah, good idea, but company architects should make a similar advice about NIH though. A dependency is a dependency, there may be tradeoffs between using third-party software and developing new in-house code, but using "Invented here" code does not vanish any kind of complexity away, it just manages it differently.

Agreed. That’s why I thought the footnote in the quote was brilliant. Guess I should’ve included that part as well: > [2] Ay, there’s the rub. I’m assuming competence. (If your company isn’t competent, well, you know what you need to do.)

The best third-party code has benefitted from industry-wide testing and fixes no single team could match. People we hire might come in already knowing it.

Competence is partly in recognizing that a problem has been solved and is no longer a good use of time, at least until we have a plan to make an improvement so great to support the redundant maintenance forever.

Re: The problem with dependency injection frameworks

#52
post #21
post #6

Hard disagree. Spending design time on dependency management is time not spend on more import design decisions.

And this is how we end up with spaghetti code. Dependency management is a critical design decision . If you're polluting your global namespace with random classes that get injected everywhere, you end up with a massive tree of intertwined dependencies. Not relying on a DI framework forces you to see that god-awful mess of spaghetti and do something about it or live with the consequences. If you're not thinking about…

Designing dependencies is critical but managing them is not.

I often found that unless you have CI you don't have flexibility at all to make more than superficial design changes. People spend days passing instances down convoluted hierarchies because they don't have any other way. Much better to use DI and start designing who needs what as a direct dependency.

A dependency injection framework also helps you encapsulate dependencies into contexts which can be used instead of global namespace. At least it should if your DI framework isn't just doing glorified singletons.

Re: The problem with dependency injection frameworks

#53
This is wrong on so many levels:

> You shouldn’t need logging everywhere, for example. Your logic code should be side-effect free, and most of the rest of your code should be throwing detailed exceptions or returning errors rather than writing to the log and returning null.

Being side-effect-free doesn't mean that you don't need logging to understand how the outputs were computed from inputs, when things do go wrong (and they will go wrong).

Code throwing detailed exceptions is great, but if you only log them at the point where they're caught several levels up the stack, you're losing a lot of context. If that particular exception is, well, actually exceptional - which should be the norm - logging it at the point where you throw it makes it much easier to debug later.

Re: The problem with dependency injection frameworks

#54
> Furthermore, dependency injection frameworks encourage you to think in terms of globals. That’s what they inject! A single, globally-configured instance of a class. Think about it. If you one day want two different instances of an injected variable, you’ll need an impact driver to express just how screwed you are. This has all kinds of knock-on effects in terms of reducing encapsulation and separating state from behavior.

What? I legitimately do not understand what this bit of the article is about. Surely the author knows of instance scopes[0] (as they are called in the DI framework I tend to use, Autofac), right? Expressing this kind of instance configuration does not require an "impact driver," whatever that means; it's just a simple matter of replacing .SingleInstance() or whatever in your bootstrapper function with an InstancePerDependency (the default) or Named or Keyed or _whatever_ kind of relationship/instance scope you want. Does this actually represent some horrible crufty sin?

[0]: https://docs.autofac.org/en/latest/lifetime/instance-scope.h...

Re: The problem with dependency injection frameworks

#55
post #20

> Every line of code in your system adds to your maintenance burden, and third-party code adds more to your maintenance burden than well-designed and tested2 code your company builds itself. Every SAAS vendor and framework advocate should have to put this on their product in black letters in a white background. Same typography as “Smoking is addictive…”

In a company, code you write yourself is a dead end. You want as little of it as possible. Staff turn over. What was a first party piece of code well understood within the company inevitably turns into a poorly documented piece of code written by a third party no longer employed, and there is no community of users to help out with problems. Write and own code which is fundamental to the business model's value proposi…

Before « move fast and break things », there used to be a thing called « documentation ». It included things like « design documents » and would ensure people were able to quickly understand a piece of code.

Re: The problem with dependency injection frameworks

#56
post #8
post #6

Hard disagree. Spending design time on dependency management is time not spend on more import design decisions.

Are you evaluating the points made by James from your context and limiting your understanding? If you work for a company where software is not a differentiator, but a cost to doing business, then using frameworks, DI or not, is probably the right thing to do. But if your code is a core part of the business, you probably don't want to give control to some third party that may screw you. All successful companies that I…

Yes, I evaluate it from the perspective of developing enterprise software where I need to designate extension points for a fluid number of team members. Only using CI can I balance the flexibility of offering the interfaces people need with the oversight needed.

Also just develop your own DI if you consider it business critical but not yet commodity (you don't do your own logging/crypto/math libs, right?).

Re: The problem with dependency injection frameworks

#57
post #53

This is wrong on so many levels: > You shouldn’t need logging everywhere, for example. Your logic code should be side-effect free, and most of the rest of your code should be throwing detailed exceptions or returning errors rather than writing to the log and returning null. Being side-effect-free doesn't mean that you don't need logging to understand how the outputs were computed from inputs, when things do go wrong…

But "logging everywhere" isn't necessary to log information about the state of the system. Libraries, for example, should not have any logging, or else it should be minimal and optional. One should prefer to return errors from library code that the application code then logs, with specific relevant context and only at a point where the log message would be useful.

Re: The problem with dependency injection frameworks

#58
post #50

The alternative of wiring up your own dependencies is pretty trivial and I really prefer it. Martin Fowler calls it a Service Locator, https://martinfowler.com/articles/injection.html#UsingAServi...

FWIW a fellow who has published books on the topic calls Service Locator an anti-pattern[1]. I take no position here as usage of any pattern is contextual but it's worth a read.

[1] https://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Patt...

Re: The problem with dependency injection frameworks

#59
post #52
post #21

Earlier quoted context omitted.

And this is how we end up with spaghetti code. Dependency management is a critical design decision . If you're polluting your global namespace with random classes that get injected everywhere, you end up with a massive tree of intertwined dependencies. Not relying on a DI framework forces you to see that god-awful mess of spaghetti and do something about it or live with the consequences. If you're not thinking about…

Designing dependencies is critical but managing them is not. I often found that unless you have CI you don't have flexibility at all to make more than superficial design changes. People spend days passing instances down convoluted hierarchies because they don't have any other way. Much better to use DI and start designing who needs what as a direct dependency. A dependency injection framework also helps you encapsula…

You seem to be missing my point. Every time you inject a dependency, you are adding another node in the graph of object interactions in your codebase. What's more, DI frameworks only specify the lifetime of injected classes in the configuration file. So if there's an object with a scoped lifetime to the class it's being passed into, this isn't apparent without checking the configuration file. If you just look at it and see that it's added via a `new` in the constructor of the class, it's immediately apparent that the scope of that object is tied to the scope of the parent class.

> People spend days passing instances down convoluted hierarchies because they don't have any other way.

And this is a sign that either: you're architecture is flawed and this dependency is probably doing something more than intended, or it's actually a global dependency and probably doesn't even need to be an object. This can be solved by cleaning up your architecture, or making the "dependency" a stateless function. One immediate example I can think of is a logging interface. I don't get why programmers think you need a "logger" (probably because of the warped idea that everything in a program must be an object). Instead, you could just make a log function that's available in the global namespace with the appropriate thread safety.

Some things are global in nature, and that's ok. Adding a convoluted DI framework to hide that fact is not ok. I like to know which interfaces are truly global instead of hunting through a codebase to find out what the mess is actually doing.

> A dependency injection framework also helps you encapsulate dependencies into contexts which can be used instead of global namespace. At least it should if your DI framework isn't just doing glorified singletons.

Sure, by hiding the lifetimes of all these objects in some massive configuration file. Now if I'm looking at class `Foo` all I see are a bunch of dependencies injected into the constructor. Any notion of which dependency is tied to the lifetime of `Foo` or global in nature or shared is now lost. Additionally, ditching the DI framework allows you to be more explicit about the lifetimes of all these constraints and formulate your code in a logical manner. One where dependency chains flow strictly one direction instead of the mess that a lot of code bases are left with.

Lastly, you didn't really refute my claim. You even seem to agree with me that managing dependencies is important, otherwise you wouldn't be using a DI framework.

My point is, this is an important design decision and should be treated as such. Using a magic DI framework allows you to hide all the messy chains that you're creating. If you ditch the framework and manually configure stuff, it forces you to really think about whether your architecture makes sense or not.

Re: The problem with dependency injection frameworks

#60
post #57
post #53

This is wrong on so many levels: > You shouldn’t need logging everywhere, for example. Your logic code should be side-effect free, and most of the rest of your code should be throwing detailed exceptions or returning errors rather than writing to the log and returning null. Being side-effect-free doesn't mean that you don't need logging to understand how the outputs were computed from inputs, when things do go wrong…

But "logging everywhere" isn't necessary to log information about the state of the system. Libraries, for example, should not have any logging, or else it should be minimal and optional. One should prefer to return errors from library code that the application code then logs, with specific relevant context and only at a point where the log message would be useful.

Libraries should still have extensive logging, but it should be such that it can be plugged easily into whatever logging framework the app as a whole is using.

In any case, DI is generally out of scope for libraries in the first place already, so I don't think the article was complaining about that.

Post reply on HN