So… You’re a Java programmer? Or .NET maybe? Do you use dependency injection?
No, seriously. Why? Think about that for a moment. Why are we going through the motions of defining boilerplate interfaces named I<real name here>, with sole inheritors called <real name here>Impl.1 Why do we sprinkle our code with annotations and configuration, destroying the traceability in the process? Why not just define the real thing once and be done with it?
MVC isn’t a reason: It’s perfectly valid to let your REST endpoints instantiate its services directly. Your code would even be strongly typed again instead of @Randomly typed.2 You would once again, for the first time in twenty years, directly know where your implementations were coming from! You would need no longer wade through six layers of generated proxy-objects, ballooning your stack track traces to ridiculous proportions. Still we all accept this crap, and more, giving up parts of our precious type system to boot. We might as well be writing in Rub… no, sorry, got carried away there. But still.
And if you answered with ‘inversion of control’, I will ask you again: Why? Because IoC is a means to an end, not a goal in and of itself. IoC is nothing more than separation of construction and execution. Those who do the wiring, don’t do the doing. So, seeing how much it costs us to get this off the ground –creating huge frameworks in the process– it had better yield something very valuable. But what? What is so precious and unique that we are all willing to give up so much to achieve it, and do the thing that developers hate most: extra work?
Some honest people might say: “We use it because everybody does”. And they’d be right. Everybody does it. It is the norm to ‘do’ IoC. You needn’t even think too much about it anymore. It is so obvious to start a project using a template, getting all those nifty frameworks configured for ‘free’ and steamrolling us into an MVC-layered, dependency injected structure. More often, the project is already under way when we join and we just go with the flow. Which is not necessarily a bad thing, mind.3 And so, following the iron laws of group think, we stopped wondering what we are doing and just do.
Most of us, of course, do know the reason behind all this madness. Decoupling. The holy grail of any architecture. We know it is important, because…
And this is where it gets weird for me. Once it’s time for the practical examples illustrating the usefulness of decoupling, many dependency injection examples resort to use cases describing some far off doom scenario that happens at most only once in an application life cycle. When you replace your database layer… If your API suddenly changes… Say what? Why do you need to resort to such draconian examples? Is decoupling so abstract that its benefits are only apparent at application scope?
It’s like decoupling got in with the bad crowd and DI tags along. It tarnished its ideals and sold the family jewels to get the laugh at the party. “You heard about the idiot who couldn’t change from Oracle to Postgres? Well, if only he’d decoupled…” Recently, a line of similarly weird reasoning popped up: “If you want to migrate to microservices…” Which is essentially the same thing.
Viewed against these use cases, dependency injection suddenly does seem like too much of a hassle! I can’t defend a daily, continual effort for something that might someday happen once. Neither should you.
Yet our use of IoC mirrors this pattern closely. We only @Inject our services into other services, @Autowiring @Singleton into @Singleton ad nauseum. And we restrict ourselves. Only the big stuff gets injected; repositories, services and facades. It seems that if it does not represent a Model, a View or a Controller, it will not get the benefit of IoC. Why is that?
Decoupling isn’t some grand scheme that happens to applications only, but to packages. Classes. Methods even. Every time you invoke a polymorphic method on an interface, you make use of decoupling. Decoupling allows us to shield our changes here from our critical sub system there, creating independence and adding a measure of safety. It helps us avoid domino bug chains and post-midnight goose chases through our code base.
Decoupling is about the small, daily separation of responsibilities and our dependency injection should support that. I will talk about achieving this small scoped injection in my next post. Until then, ask yourself: Why are we using dependency injection again?
1 Have you ever really tried to pronounce Impl? It sounds ridiculous.
2 Do you remember the promise that annotations would eliminate stringly typed code? Well, @They [Did]. Aren’t you glad?
3 Except for the not-thinking part. That is always a bad thing.