Use constructor injection to make dependencies explicit and classes easier to test.
The core idea
Dependency injection means a class receives the collaborators it needs instead of creating them internally. Constructor injection makes required dependencies visible and supports immutable fields.
A practical way to approach it
Define a small service interface when it represents a useful boundary, inject it through the constructor, and keep the class focused on one responsibility. Use test doubles for collaborators that would otherwise require a database or network.
Try it in a small project
A billing service can receive a payment gateway and an invoice repository. In a unit test, provide fake implementations and verify the decisions without making a real payment or database call.
Common mistakes to avoid
Do not create an interface for every class without a reason. The goal is clear dependencies and replaceable boundaries, not abstraction for its own sake.
Key takeaway
Keep the first version small, make your assumptions explicit, and verify the result with a repeatable test. For production use, check the current official documentation for the tools and versions you choose.