$container->get('app.mailer') stopped working this week. Symfony 3.3 is out, and in the new configuration services are private by default. The container refuses the call. Everyone is writing about autowiring. This is the change I like more.
That call was always a smell. A class that pulls dependencies from the container by string id is a class with secrets. Its constructor says nothing, and to test it you boot half the framework, because it may ask for anything at any moment.
Constructor injection makes the same code boring, in the good sense:
public function __construct(Mailer $mailer, LoggerInterface $logger)
{
$this->mailer = $mailer;
$this->logger = $logger;
}
The signature is the documentation. The test is new UserNotifier($mailerMock, new NullLogger()). No kernel, no container, no fixtures for things you do not use.
Errors move to a better place too. A wrong service id used to explode in production, on the one request that hit that branch. Now a missing dependency fails when the container compiles. Cache warmup dies on the deploy server, traffic never sees it. A loud deploy beats a quiet incident.
The bigger point is what the container becomes: a wiring machine that runs before your code and then gets out of the way. An implementation detail.
I still have $container->get() in a few old console commands. They work, the old config is untouched, the container still hands them whatever they ask for. I am just no longer proud of them.