DI seems like some sort of job security by obscurity.
> then you're probably leaving some workflow efficiency on the table
Typical HN to assume what the best workflow efficiency is, and that it mostly hinges on a specific technology usage :)
Imagine I'd claim that since you're not using nrepl and a repl connected to your editor, you're leaving some workflow efficiency on the table, even though I know nothing about your environment, context or even what language you program in usually.
Usually on the third time someone recommends {X} I would have looked into it and formed my own conclusions with first hand experience.
The issues are still there. You can't just "go to definition" of the class being injected into yours, even if there is only one. You get the Interface you expect (because hey you have to depend on Interfaces because of something something unit-testing), and then see what implements that interface. And no, it will not just point to your single implementation, it'll find the test implementation too.
But where that "thing" gets instantiated is still a mystery and depends on config-file configured life-cycles, the bootstrapping of your application, whether the dependency gets loaded from a DLL, etc. It's black-box elephants all the way to the start of your application. And all that you see at the start is something vague like: var myApp = MyDIFramework.getInstance(MyAppClass); Your constructors, and where they get called from is in a never-ending abyss of thick and unreadable framework code that is miles away from your actual app. Sacrificed at the alter of job-creation, unit-testing and evangelist's talk-resume padding.
Yes, I can? At least Rider can jump to the only implementation, no questions asked.
> And no, it will not just point to your single implementation, it'll find the test implementation too.
It will, but is it a problem to click the correct class from a list of two options?
Yes, the comments about "$25 name for a 5c concept" ring true when you're looking at a toy example with constructor(logger) { .. }.
Then you look at an enterprise app with 10 years of history, with tests requiring 30 mocks, using a custom DI framework that only 2 people understand, with multiple versions of the same service, and it feels like you've entered another world where it's straight up impossible to debug code.
This situation isn't unique when using DI (although admittedly DI does make using interfaces more common). However, that's what the "go to implementation" menu option is for.
For a console app, you're right that a DI framework adds a lot of complexity. But for a web app, you've already got all that framework code managing controller construction. If you've got the black box anyways, might as well embrace it.
I remember trying to effectively reverse-engineer a codebase (code available but nobody knew how it worked) with a lot of DI and it was fairly painful.
Maybe it was possible back then and I just didn't know how ¯\_(ツ)_/¯
In a Spring application there are a lot of (effective) singletons, the "which implementation of the variable that implements Foo is it" becomes also less of a question.
In any case, we use Spring on a daily basis, and what you describe is not a real issue for us.
Also, what I think is also important to differentiate between: dependency injection, and programming against interfaces.
Interfaces are good, and there was a while where infant DI and mocking frameworks didn't work without them, so that folks created an interface for every class and only ever used the interface in the dependent classes. But the need for interfaces has been heavily misunderstood and overstated. Most dependencies can just be classes, and that means you can in fact click right into the implementation, not because the IDE understands DI, but because it understands the language (Java).
Don't hate DI for the gotten-out-of-control "programming against interfaces".
It's technically a flaw of using generic interfaces, rather than DI. But the latter basically always implies the former.
Edit: upon rereading I realize your point was about reading code, not writing it, so I guess that could be a different use case...
There's nothing wrong with using an IDE most of the time, but building dependence on one such that you can't do anything without it is absolute folly.