Edit (27 Apr 16): I should've probably led by saying that I am a big fan of Inversion of Control. That also means I'm a decent fan of DI. But I'm also a fan of not over-engineering, and somehow the "culture" around DI is one where over-engineering seems to be fairly commonplace (see the Joel on Software post I talk about, below -- he's not talking about DI specifically, but I think you can inject it (hahahahaha) into his generic critique in this case).

I love separation of concerns. What I don't love is hipster programming. If you're never going to reuse something, and it's significant to insane work to abstract it, don't. If abstraction was a bit more, knock yourself out, but, for instance, QueryOver in NHibernate when you're so deeply invested in SQL Server there's no great business case to leave, stop it. If your devs know SQL, stop it already.

A better solution to a potential change in stack is to become familiar with microservices, and use them appropriately. The only place I can think of where this sort of gross abstraction might make some sense is when your stack is just getting off of the ground. But then it's no big deal to rip it all up if your old, crufty C# guy leaves and is replaced by Node Grrrl.

Was doing some quick reading before talking about Dependency Injection tomorrow, and ran into a link from an SO question to a Joel on Software post I don't think I've run across before, Don't Let Architecture Astronauts Scare You:

These are the people I call Architecture Astronauts. It's very hard to get them to write code or design programs, because they won't stop thinking about Architecture. They're astronauts because they are above the oxygen level, I don't know how they're breathing. They tend to work for really big companies that can afford to have lots of unproductive people with really advanced degrees that don't contribute to the bottom line.

A recent example illustrates this. Your typical architecture astronaut will take a fact like "Napster is a peer-to-peer service for downloading music" and ignore everything but the architecture, thinking it's interesting because it's peer to peer, completely missing the point that it's interesting because you can type the name of a song and listen to it right away.

All they'll talk about is peer-to-peer this, that, and the other thing. Suddenly you have peer-to-peer conferences, peer-to-peer venture capital funds, and even peer-to-peer backlash with the imbecile business journalists dripping with glee as they copy each other's stories: "Peer To Peer: Dead!"

The Architecture Astronauts will say things like: "Can you imagine a program like Napster where you can download anything, not just songs?" Then they'll build applications like Groove that they think are more general than Napster, but which seem to have neglected that wee little feature that lets you type the name of a song and then listen to it -- the feature we wanted in the first place. Talk about missing the point. If Napster wasn't peer-to-peer but it did let you type the name of a song and then listen to it, it would have been just as popular. [emphasis mine, as usual -mfn]

Man, that's good.

Honestly, Wikipedia currently does an excellent job explaining the pros and cons of Dependency Injection. Here are a few from each...

[Some] Advantages [taken from Wikipedia's entry on DI]

  • Dependency injection allows a client to remove all knowledge of a concrete implementation that it needs to use. This helps isolate the client from the impact of design changes and defects. It promotes reusability, testability and maintainability.[20]
  • Dependency injection allows concurrent or independent development. Two developers can independently develop classes that use each other, while only needing to know the interface the classes will communicate through. Plugins are often developed by third party shops that never even talk to the developers who created the product that uses the plugins. [that's give or take what the last point said -mfn]
  • Dependency Injection decreases coupling between a class and its dependency.[21][22]

[Some] Disadvantages [taken from Wikipedia's entry on DI]

  • Dependency injection creates clients that demand configuration details be supplied by construction code. This can be onerous when obvious defaults are available.
  • Dependency injection can make code difficult to trace (read) because it separates behavior from construction. This means developers must refer to more files to follow how a system performs.
  • Dependency injection typically requires more upfront development effort since one can not summon into being something right when and where it is needed but must ask that it be injected and then ensure that it has been injected.
  • Dependency injection can cause an explosion of types, especially in languages that have explicit interface types, like Java and C# [23]
  • Dependency injection forces complexity to move out of classes and into the linkages between classes which might not always be desirable or easily managed.[24]
  • Ironically, dependency injection can encourage dependence on a dependency injection framework.[24][25][26]

Okay, I admit it. I kept all of the disadvantages up there.

I have worked with a system that overused DI before, and we had it so far upstream of our entities and controllers that you could work for months without ever coming into contact with the construction of a repository. That's not necessarily good. The "explosion of types" was definitely a problem, which means that over-abstraction was as well. "DI! DI! We can run the whole world with DI! DI venture capital funds!" /sigh

I like interfaces. And my C# rdbms finally talked its way to using DI. I'm (slowly; I haven't had much time to work with it right now, as I don't have a product for which it's an absolute requirement) factoring it into a portable library, and that means I have to inject a class in that writes to files. Not a big deal, and certainly The Right Way to do it.

What I dislike is when the DI is so abstracted that you're essentially cargo culting your way through repo instantiation, where you can create a repo from scratch and the inheritance hides that some base class is doing all the DI for you. That is, it's way too easy to end up in that last "con" from above: "Ironically, dependency injection can encourage dependence on a dependency injection framework."

Wikipedia has an interesting link to some Uncle Bob Consulting jive. I haven't closely read it all, but some of this rings true on first skim...

But Uncle Bob, you’ve violated DIP by creating concrete instances!
I don’t want a bunch of secret modules with bind calls scattered all around my code. I don’t want to have to hunt for the particular bind call for the Zapple interface when I’m looking at some module. I want to know where all the instances are created.

But Uncle Bob, You’d know where they are because this is a Guice application.

I don’t want to write a Guice application. Guice is a framework, and I don’t want framework code smeared all through my application. I want to keep frameworks nicely decoupled and at arms-length from the main body of my code. I don’t want to have @Inject attributes everywhere and bind calls hidden under rocks.
But Uncle Bob, That means I have to use new or factories, or pass globals around.

You think the injector is not a global? You think BillingService.class is not a global? There will always be globals to deal with. You can’t write systems without them. You just need to manage them nicely.
I like this because now all the Guice is in one well understood place. I don’t have Guice all over my application. Rather, I’ve got factories that contain the Guice. Guicey factories that keep the Guice from being smeared all through my application. What’s more, if I wanted to replace Guice with some other DI framework, I know exactly what classes would need to change, and how to change them. So I’ve kept Guice uncoupled from my application.
But Uncle Bob, don’t you think Dependency Injection is a good thing?

Of course I do. Dependency Injection is just a special case of Dependency Inversion. I think Dependency Inversion is so important that I want to invert the dependencies on Guice! I don’t want lots of concrete Guice dependencies scattered through my code. [emph mine again -mfn]

That sounds about right, though it also seems to create a situation where you've got at least one of the "secret module" situations he was trying to avoid.

The takehome message when considering Dependency Injection? Never outsmart yourself. Or, as Zakas put it a while back, "don't be too clever".

Labels: , ,