onsdag 22. februar 2012

Kill Your Dependency Injection Container

First of all, I am quite found of the Inversion of Control pattern. I have been a heavy user of various IoC frameworks for years (almost a decade now). Earlier I never really questioned my use of such tools. They got me going pretty fast. But a year ago I read the blog post Simple-Hickey by Uncle Bob and saw the video published at InfoQ by Rich Hickey. This input really got me thinking, reflecting upon what I have experienced in various projects over the years.

What Rich Hickey discusses is known as "simple over easy" or "simple made easy". Hickey’s concern is “
that we have a culture of complexity. That when programmers are given a task, they race ahead and write masses of tangled code, using “easy” frameworks and tools, without giving the problem due thought. That we confuse easiness, with simplicity. (e.g. Rails is easy, it is not simple.)”.
Quote taken from Uncle Bob’s blog post.

So back to my own experience. Over the years, have I ever fought with the limitations in a container? My own limited knowledge of a specific container? Have I ever had to write container-specific integration code to get the container to do exactly what I wanted for a specific odd requirement? Did many or most of my co-workers have sufficient knowledge about these tools? Did a framework “force” me to solve a set of problems in a way I did not find satisfactory? To be honest, both me and several colleges over the years have struggled with various tools and frameworks. We have spent days researching what is happening in there, why something goes wrong or not according to plan. We have made forks, extended various parts to customize container configuration behaviour or simply created infrastructural glue to get the framework or the specific tools do what we needed them to do. One example that suddenly came to mind; several containers offer support for object life time management. This is usually trivial to set up in the configuration and start to use. What if you later discover that the default implementation does not play well with your needs or you need a special kind of life-time policy that seems impossible to specify within the rules of the framework? You might end up opening that big black box to fix the issues, often discovered at customers in production code. This is infrastructural jamming, which sure can be fun, but not very productive for the company. If all the developers in the team had full understanding of what happened in the framework, could easily debug and have the confidence to change the code, then maybe the problems could have been fixed in a breeze?

IoC frameworks may be easy to use, getting you "up-and-running" in a short amount of time. But the code inside the framework might be quite complex, and is not simple. Alright, all the slogans tell us to not be too concerned about this complexity, these tools have been heavily tested, have a wast user mass, proven themselves worthy over time, so such complexity can be safely abstracted away. And you should not reinvent the wheel, creating a poor, bug-prone implementation of the same functionality. These are all, in many situations, valid arguments. But sometimes you should stop and think if you really need that big library or framework. I have worked with applications where the overall code footprint from the IoC framework were much bigger than the application code itself. Truthfully, the complexity in these applications could have been reduced a lot by removing the IoC container, as such small applications' configuration need often are limited and trivial. Many of these frameworks are pretty wast, containing code for handling many different concerns. This could be tools for:
  • Inversion of Control
  • AOP
  • Scheduling
  • Web Framework Integration
  • Messaging
  • UI Component Frameworks
  • Plugin Architecture
  • Life Time Management
  • Threading
  • Serivce Integration (WCF, COM+, .Net Remoting)
  • Testing
  • ORM Integrations
  • Logging
  • Synchronization
  • Events
  • ESB Integration
  • Security
  • Various flavours for configuration
  • Etc….

Of course, these frameworks are split into modules, so you don’t have to use everything. But from my own experience, when one starts to use one module, it is easy to drag in another at a later point in time. Especially if you take company software policies into consideration. If you only use a small fraction of the functionality offered from a module, then you might consider whether you should use that library at all. Another thing to consider, when using some of the features listed above, programmers might not really take the time to acquire ownership by actively understanding the complexity of a problem. You might hear; ”This will be solved by the framework”! At several occasions, I have seen that such abstractions can hurt productivity and customer satisfaction at a later point in time. Some of these complexities cannot really be abstracted away. Sooner or later one must understand what is going on and have a strategy for change/refactor if needed. The hurting really starts when something goes wrong (aka bugs or other deficiencies) and programmers have to dig into the implementation details of the framework. So, if you are using a framework or an IoC container, you and your team must be prepared to invest a lot of time and knowledge into this component to avoid future problems when things get complicated. Some complexity cannot be abstracted away, no matter what tools you use.

Currently I’m working on a green-field project specifying the architecture and the core infrastructure of the application. I’m the only programmer working on the code. In a few months we will scale up and hire in more programmers. I want the application to be simple, very simple. I do not want to drag in third party frameworks if I can avoid it. Any third party library will add amounts of knowledge and complexity that future co-workers need to handle. This will in turn reduce the selection of programmers I can choose from. So how can I cope with this? Should I reinvent the world? Of course not, I need to find a balance. I can find patterns that solve most problems, I can time box an experiment implementing a pattern to see if that serves my needs. I need to ask critical questions, so for instance, what do I want my IoC container to be able to do. What should be it’s main responsibility. Should it offer support for service integration, life time management, session management, aspect oriented programming and so forth? If I had answered yes on all these questions, then I would have opted for a full fledged container like Unity, Castle Windsor or Spring.Net. But remember, I want this to be kept simple, so I only want my IoC container to do one thing; assemble object graphs. All other concerns I must solve with other means, not abstracted away and handled by my container.

So with this in mind, I sat down one evening and spent some hours writing my own light-weight implementation of an IoC container. The dependency injection code amounts to about 250 lines of code to get the heavy lifting done. In addition there is a support class around 130 lines of code facilitating assembly scanning. That is pretty much everything needed to get similar code configuration as presented by the Spring.Net framework to work. Now, my experiment is that these 400 lines of in-house code will not cause nearly as much trouble as an IoC framework would. The reasons for my hyopthesis are:
  • My self rolled Dependency Injector is tiny
  • It has one well defined responsibility
  • It is simple to get an overview of ALL the code
  • It has a 95% unit test coverage
  • Since it is simple and small with a good test coverage, programmers will change the code with confidence
  • Within a short period of time, programmers will take active ownership and responsibility over the code.
  • Since the component can only do one thing, all other usual problems must actively be addressed by the programmers themselves. They cannot rely on the Dependency Injector to abstract away and solve their problem.

I’m exited to see what the future will bring out of this empirical experiment.


The heading might at first glance sound pretty harsh. It’s because I want your attention by provoking all the frameworks and IoC container lovers. The most important thing to take away from this is to really think thoroughly through if you really need that big IoC framework? Maybe you can be more productive by going really lightweight, choosing an IoC container with a small code footprint. Less code, less complexity...


If you want to play with the code, then download the DependencyInjector here!

Ingen kommentarer:

Legg inn en kommentar