Hacker News new | ask | show | jobs
by sly010 4739 days ago
I understand dependency injection, but I never understood dependency injection frameworks. For me the whole point of DI is explicit composability. There should only be a few well defined places in my application where all my classes are assembled into a working program. If my classes cover my domain nicely, I should be able to compose all kinds of programs (including tests) and reuse code.

If my objects get injected to each other automatically by their name then my dependencies are just as predetermined, which results in coupled code, except the coupling is now difficult to read or modify.

Once I decided I am only using explicit injection, I don't need a framework, I just instantiate my classes in the right order and pass them into each other. What can possibly be more pythonic than that?

I have the same issue with Angular.js btw. They are trying to make is easy to start by adding dependency injection magic, but in reality all I see is "this can't possibly work (and indeed it doesn't)", so I just need to spend more time digging and understanding the magic before I can start writing ANY code.

Every documentation should start by explaining the explicit way of doing things, then adding the sugar afterwards.

import this

1 comments

I'd prefer by type to by name, but there's still a small decoupling advantage: suppose you have some componentA in your application and you want to add some more functionality that depends on some serviceB. What DI should make possible is you modify only componentA, to state that it now depends on serviceB, and as long as you have a serviceB available then you don't need to change your "wire up my application" code at all (but of course if there is no serviceB then this should fail as fast as possible and tell you to fix it).

/has just written a compile-time DI framework in scala using type-level programming. It's pretty sweet except for the compile times.

> What DI should make possible is you modify only componentA, to state that it now depends on serviceB, and as long as you have a serviceB available then you don't need to change your "wire up my application" code at all

So... instead of modifying just the "wire up my application" code, you modify just the componentA code. In return you get to fill your code with magic so nobody can see where serviceB is created.

You always have to modify componentA - you're adding a new feature to it, and declaring that it takes a serviceB. If you're not using DI you have to change your "wire up my application" code as well.

The choice is between writing the wiring code yourself, which means a lot of boilerplate that's basically irrelevant to the functionality of your application, or have it happen implicitly, which yes involves a certain amount of magic. But even polymorphic functions are "magic" on some level.

In practice, the automatic "wiring-up" often breaks, and in any sizable system it quickly becomes very difficult and time consuming to track down and fix such breakages.

Things can become even worse when the libraries or frameworks you're using end up having bugs in them that interfere with the dependency injection they're performing.

Too many times I've seen very experienced and capable developers lose hours, and even days in some cases, dealing with dependency injection issues. Just one of these incidents, which will happen in any serious project, will far exceed the time and effort it would've taken to write the equivalent code by hand.