A blog post from Oren Eini aka Ayende Rahien, inspired by a twitter discussion about using vanilla DI vs a full-blown IoC container:

For stuff that doesn’t change very often, we do things manually. For the things that we add a lot of, we make it as smooth as possible.

Ultimately, it comes at one of my favorite software architecture guidelines: use the right tool for the right case. We are so used to use DI Containers - like, it’s taken for granted when you use dotnet new - that we don’t even think about whether it is really necessary or not, and sometimes, a container might not be the best tool for the job.

What we have done is optimize one aspect, which we deal with often, while manually dealing with the stuff that is rarely changing. That means that if I do need to make a change there, the level of magic involved is greatly reduced.

^ this. Sometimes, on critical pieces of the software, adding more layers of abstraction can cause issues in the long run - maintainability, performance, etc. - because it works “magically”, but you can’t debug magic.