One issue many people have with Dependency Injection is the way you can no longer jump to an implementation of a dependency in an IDE and see what it actually does or make changes to it. This is indeed a side effect of moving towards much more loosely coupled code. If you are injecting a dependency and type hinting it with an interface, then you cannot jump straight to the particular implementation without looking at the DI configuration to see what implementation is being used.
This is not a bad thing though in my opinion, it is just part of moving away from coding to an implementation instead coding to an interface. The code you are working on does not know about the details of the implementation only its public methods, so in many ways it helps to, in effect, only know that yourself when working on that class. A big advantage of Dependency Injection is shifting the decision of what implementation to use out of the code and into a separate configuration, which is why it is a good thing to be ignorant of this configuration when coding.
The inverse way of looking at this issue is that if you are not using Dependency Injection to pull all of this configuration out of the code then you cannot get a top down perspective of how the application is wired together without digging through the code itself. By separating the configuration from the classes themselves you can more easily get an overview of how all the classes work together in an application. Once you have made this shift in your way of thinking then it seems less problematic to not be able to jump to the implementation when working in the code.
If you are working with Symfony2 then as well as just looking at the services config files there are some great tools to get an overview of the services.
Out of the box there is a console command that will print out a list of all the services in the service container.
php app/console container:debug
[container] Public services
Name Scope Class Name
acme.demo.listener container Acme\DemoBundle\ControllerListener
annotation_reader container Doctrine\Common\Annotations\FileCacheReader
assetic.asset_manager container Assetic\Factory\LazyAssetManager
assetic.controller prototype Symfony\Bundle\AsseticBundle\Controller\AsseticController
assetic.filter.yui_css container Assetic\Filter\Yui\CssCompressorFilter
assetic.filter_manager container Symfony\Bundle\AsseticBundle\FilterManager
assetic.request_listener container Symfony\Bundle\AsseticBundle\EventListener\RequestListener
cache_warmer container Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate
data_collector.request container Symfony\Bundle\FrameworkBundle\DataCollector\RequestDataCollector
database_connection n/a alias for doctrine.dbal.default_connection
debug.event_dispatcher n/a alias for event_dispatcher
doctrine container Symfony\Bundle\DoctrineBundle\Registry
doctrine.dbal.connection_factory container Symfony\Bundle\DoctrineBundle\ConnectionFactory
doctrine.dbal.default_connection container Doctrine\DBAL\Connection
doctrine.odm.mongodb.cache n/a alias for doctrine.odm.mongodb.cache.array
doctrine.odm.mongodb.cache.array container Doctrine\Common\Cache\ArrayCache
doctrine.odm.mongodb.default_configuration container Doctrine\ODM\MongoDB\Configuration
#--
You can also specify a service by id to get more detailed information:
php app/console list assetic.asset_manager
[container] Information for service assetic.asset_manager
Service Id assetic.asset_manager
Class Assetic\Factory\LazyAssetManager
Tags -
Scope container
Public yes
Whilst the console command is certainly helpful, especially for resolving service ids to actual class names, there is more information it would be helpful to be able to view. Johannes M. Schmitt's Debugging Bundle provides more useful tools for working with services in Symfony2. Once installed it adds a new section to the web profiler called service container
where you can view information about the services being used in your application.
There is an overall graph of the services, this is split into pages since a typical service graph will include a lot of services:
You can also view the services as a list. The list shows what calls were made to include services including where they originated from, the type of injection which took place, e.g. during construction and whether it was constructed for this call or already existed:
Most helpfully if you are trying to find out more about a particular service, you can search by service name with an auto-complete facility:
You can then view the outgoing services for the chosen service, that is the services that are injected into that service and their dependencies:
You can also choose to view the incoming services, that is the services that the chosen service is injected into.
This all helps to visualise the use of the various services and how they interact with each other. This is a much more difficult task when the dependencies are not injected into classes. You may be able to see what dependencies a class has just from the code but you cannot see which other classes use those dependencies as well nor can you easily see what code uses the class you are working in.