Dependency Design
Direction, inversion and injection — taught as three different ideas rather than one, because conflating them is how teams end up with a container and no boundaries.
Stable, high-level policy should not depend unnecessarily on volatile, low-level detail. The word "unnecessarily" is doing almost all the work.
Business policy defines the interface; infrastructure implements it. Both arrows point at the middle — and none of this requires a container.
An object receives its collaborators instead of constructing them. That is the whole idea, it needs no library, and it is the third of three things people call DIP.
Take collaborators as constructor parameters and the type system enforces that a constructed object is a usable one. No framework required, and the parameter count is a design signal.
A global registry objects pull dependencies out of. It makes dependencies implicit — which defeats local reasoning and moves whole classes of error from compile time to run time.
Somebody has to construct the object graph. Doing it in one deliberate place is the design decision; doing it with a container is a separate, later, optional one.
Only some dependencies are worth inverting: the ones that change, that are slow, that have side effects, or that are non-deterministic. The rest should be called directly.