Live data from Hacker News

The problem with dependency injection frameworks

jamesshore.com

41–50 of 92 posts

Re: The problem with dependency injection frameworks

#41

At work, all of my function has at most 3 parameters: deps (dependencies), params (for parameters), ctx (for context), which covers all of my use cases, easy to test, debug, isolate.

This is the equivalent of a relational database schema where there's only a couple of tables with columns such as: (EntityId,RowId,ColumnId,Value).

In very rare cases this type of design is required, but the key word here is "rare". It shouldn't be the norm for ordinary apps such as typical web apps! If you find yourself doing this type of thing regularly, then you've likely made some sort of mistake.

Re: The problem with dependency injection frameworks

#42
> “This implementation is difficult to unit test.” Horsepucky.

No, this implementation is difficult to unit test. The rebuttal, “Just make a constructor […]” changes the implementation. The author’s zeal to decry DI frameworks has made him forget for a moment that constructor injection looks the same whether a framework is involved or not.

Re: The problem with dependency injection frameworks

#43
post #35

I advise to try build-time DI frameworks, like Dagger, see how it generates the glue code, and then ditch it and write the same glue code yourself. Yes, build-time cannot do tricky cases (when it depends on some runtime-only thing), but I'm yet to see any case of that. Build-time is also easier on JIT compiler, as there's no reflection involved in runtime, for VM it's all just hardcoded.

I prefer koin instead. It does no magic. It's simple function calls packaged up as a nice Kotlin DSL. Pure declarative. Easy to debug. It does not even use reflection. I've used it with ktor, and with kotlin-js in a browser (it's a kotlin multi platform library). There's basically no overhead relative to the code you'd otherwise be writing manually. I'm not an Android developer but I hear it's pretty popular there as well.

I've use spring dependency injection as well. It's actually not that bad if you use constructor injection only. No Autowired in any code I touch. You don't need it. Constructor injection makes everything easy. And it makes it easy to test as well. My unit tests do not depend on Spring. There's no need. And with the recent declarative way of creating beans, it can be pretty similar to koin.

I've done some diy dependency injection as well on occasion. It's not that hard. Just separate your glue code (construction) from your logic. Your main function would be a good place. Constructors don't get to do work. I've seen some bad frontend code that violate these rules and it's a mess where nothing is testable because trying to run any bit of code you end up with half the code base firing up. Lack of a framework is no excuse for bad design.

Re: The problem with dependency injection frameworks

#46
The Dependency injection pattern is just not that great in general. There are alternative patterns that are better.

It is not a problem with frameworks. Think about it. If the pattern was good, then a good framework must exist. If no good framework exists then logically it is very likely that Something is wrong with the Pattern itself.

Anyway the reason why DI is bad is because it's too complex. In your program, you should have logic, and then have data move through that logic to produce new data or to mutate.

When you have dependency injection, not only do you have data moving through logic, but you have logic moving through logic. You are failing to modularize data and logic and effectively creating a hybrid monster of both data and logic moving through your program like a virus.

The pattern that replaces dependency injection in this: functions. Simple.

Have functions take in data and output data then feed that data into other functions. Compose your functions into pipelines that move data from IO input to IO output. If you want to change the logic you simply replace the relevant function in the pipeline. That's it.

One very typical pattern is to have IO modules get injected into modules so that one can replace these things with Mock IO during unit testing. With function pipelines things like IO modules should be IO functions, not modules injected into other modules. When you want to unit test your function pipeline without IO simply replace the IO functions with other mock IO functions. That's it. I will illustrate with psuedo code below.

    compose(a,b) = lambda x : a(b(x))
    a * b = compose(a,b)
    pipeline = IOoutput * x * y * z * f * IOinput
    pipeline()

The above is better then:

    class F(IOinputClass):
         f(x) = IOinput()
    
    class Z(F)
         z(x) = F.f(x)

    class Y(Z)
         y(x) = Z.z(x)

    class X(Y)
         x(x) = Y.y(x)

    class IOOutput(X)
         print(x) = print(X.x(x))

   pipeline = X(Y(Z(F(input))))
   pipeline.print()
You can see the second example is more wordy and involves unnecessary usage of state when you inject logic into the module. (I left out the constructor that assigns the class instance to state but the implication is there).

Dependency injection is a step backwards. It decreases modularity by unionizing state with logic. It's a pattern that became popular due to the prevalence of using classes excessively. If you can I would avoid this pattern all together.

Re: The problem with dependency injection frameworks

#47

At work, all of my function has at most 3 parameters: deps (dependencies), params (for parameters), ctx (for context), which covers all of my use cases, easy to test, debug, isolate.

This is the equivalent of a relational database schema where there's only a couple of tables with columns such as: (EntityId,RowId,ColumnId,Value). In very rare cases this type of design is required, but the key word here is "rare". It shouldn't be the norm for ordinary apps such as typical web apps! If you find yourself doing this type of thing regularly, then you've likely made some sort of mistake.

You're making assumption ?

The code is absolutely maintainable, simple by design, simple to test in isolation , simple to debug in isolation, simple to scale features, simple to replace,...

I'm not sure what you want more for a production-ready code.

Re: The problem with dependency injection frameworks

#48
post #26

I remember the first time I used Spring and I had to debug a traceback that included not a single line of code I had written. It was hell. I almost gave up being a programmer. Even today I work with half baked frameworks that have the same problem and I hate it. The difference is that when something like, say, a web framework does this it is buying me something valuable in exchange for the frustrating occasions when…

>DI frameworks that do this buy you nothing of value except the paternalistic approval of people who dont have the imagination to think beyond unit tests.

I know I need to hop off the internet for a while whenever I hit a comment arrogantly asserting such ignorance.

Post reply on HN