I noticed usually DI is not necessary at runtime, but rather at compile (or boot) time.
In practice I noticed I'm ok with direct dependency as long as I can change the implementation with a compile time variable. For the tests, I use an alternative implementation, for development another.
I don't swap an implementation for another within the same code. It is an option, but it happens so rarely that it seems absurd optimizing for it.
So, I like dependency injection as a concept, but I avoid it to reduce complexity. The advantage is that you can get by with a lot more "global" code. In Go this is particularly nice since DI is really nasty (struct methods have various limitations)
In practice I noticed I'm ok with direct dependency as long as I can change the implementation with a compile time variable. For the tests, I use an alternative implementation, for development another. I don't swap an implementation for another within the same code. It is an option, but it happens so rarely that it seems absurd optimizing for it.
So, I like dependency injection as a concept, but I avoid it to reduce complexity. The advantage is that you can get by with a lot more "global" code. In Go this is particularly nice since DI is really nasty (struct methods have various limitations)