What is dependency injection, and why does it matter more than it sounds like?
Dependency injection means a class is given the objects it needs instead of creating them itself. Instead of a report service calling new to build a data source, the data source is passed into the constructor. The class then depends on a contract rather than on a construction detail.
It matters because of what it makes possible. The class can be handed a different implementation without being changed, which is how you support a real one and a fake one in the same codebase. It can be tested without the real infrastructure, so the test runs in milliseconds and never fails because a database was busy. And the dependency becomes visible in the class's signature, so a reader can see what it needs without reading its body.
There is a second, quieter benefit. When a class builds its own collaborators, the construction of the whole object graph is hidden in the middle of the code, and it grows as new requirements appear. Injecting instead means the wiring lives in one place, usually a composition root, where the entire structure of the application can be read at a glance.
The trade-off is that the wiring code has to live somewhere, and someone has to write it. That is real work, but it is work that would have happened anyway, and doing it explicitly tends to produce a better answer.