Breaking Up with IoC Containers

Straight Up
 
I've stopped using or caring about IoC containers. I used to use them because they were so quick and easy and they kept my code looking pristine and beautiful. Now I do manual dependency injection and the results on non-trivial systems are very interesting and look even more beautiful. At the end of the post we'll examine a little bit more of why I left them behind. In the meantime, I've probably left you wondering what the heck I do to keep things from getting out of hand. "What about the times when you have to inject a dependency through five other objects before it gets to where you need it??" Yeah, we'll get to that.
 

Focusing the Discussion
 
The statements I make in this article assume the following:
  1. You know what an IoC container is.
  2. You only use an IoC container to clean up your dependency injection.
  3. You aren't writing prototypical, throw away code.
  4. The system under question is not an ideal of perfection. It's just a realistic system where I can concretely show the product of applying this abstract idea.
Now, the problem domain: The code in this post was for a Twitter/IM client I was building for a while. Its point was to unify all of your messaging clients into one in a way that made the different clients a non-concern to the user. It was about unifying and simplifying. It has been about a year since I've touched this code so when I first came back and looked at the IoC declaration to re-figure out the lay of the land, I was underwhelmed.
 

Here's my original IoC container declaration:

 
The problem I have that needs solving is how to make my code into living documentation that describes itself long after I've forgotten about it.
 
The Hot New Thang I Replaced IoC With
 
That code really doesn't tell me anything useful about my system. "Wha- HUH?!" you say. Seriously, I get an idea of what the objects are in my system and how they correlate with my interfaces, but what about how the objects are used by one another? The power of OOP lies in graphs of objects. A graph is nothing if not a precise way of storing the interrelationships between individual elements. 
 
So what's the alternative? Ditching IoC and wiring everything together by hand. Ok, ok. I know. It sounds extreme and it sounds painful. Let's address some of what may seem to be pain. Here we can answer the earlier question of what do you do when you need to inject an object through several layers... you won't need to. The reason you've had to do this in the past is because you instantiated objects within other objects. By giving just one class this responsibility, you prevent that from ever happening again. Just pulling all of the dependencies up to the top most level isn't what this is all about though. There's still pain and, as you can see from this example, it does very little to add to the clarity:
 
So after looking at the entire system's wire up and revisiting the different classes I came up with some more appropriate naming:
 
That's the new hawtness. Sitting down, thinking of the code and how the different objects work together and naming them in a way that binds them into a cohesive overarching vision.
 
One of the key principles of OO design is cohesion afterall. Prior to getting all of these objects together and seeing how they were interconnected I didn't really see that they weren't cohesive. The various objects weren't named in a way that illustrated their cohesion with the rest of the system and I didn't have an easy way of seeing them all related to each other. 
 
A key concept that comes out of this is that code is a form of literature. Donald Knuth says,
 
"I believe that the time is ripe for significantly better documentation of programs, and that we can best achieve this by considering programs to be works of literature...

Let us change our traditional attitude to the construction of programs: Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do.

The practitioner of literate programming can be regarded as an essayist, whose main concern is with exposition and excellence of style. Such an author, with thesaurus in hand, chooses the names of variables carefully and explains what each variable means. He or she strives for a program that is comprehensible because its concepts have been introduced in an order that is best for human understanding, using a mixture of formal and informal methods that reinforce each other."

 Donald Knuth. "Literate Programming (1984)" in Literate Programming. CSLI, 1992, pg. 99
 
By declaring my object graph in a single spot a human can read it, can see how the different objects depend on and relate to one another, I have a great spot to introduce a new programmer into my system. It isn't necessarily easy, but it will be a more thorough and thoughtful treatment on the system than the original IoC declaration. If I had a better job of adhering to DDD principles in this system, I'd like to think this wire up would be even more valuable.
 
Before you go too long thinking that if I would have just taken the same amount of time with my IoC container I could've fixed the issues with it here's a sample of what it looks like AFTER renaming the classes:
 
On top of that, even if it did read exactly the same, if I am only using it for dependency injection and you don't believe it will add more value to the system's understanding (and I can't believe that you would argue with that) then it's adding superfluous complexity to my project.
 
Having a Conversation with Your Code
 
There may come a time when you try to employ these ideas on a system you're building and it may seem too difficult to get this working. Like writing a book, technical manual, blog post, etc. this technique is an art. The technique itself is not the problem, except of course when it is. To know for sure you need to have a dialog with yourself. Look for the root cause of your difficulties, they'll usually align with places where you've bucked the corner stone OO principles (encapsulation, cohesion, and polymorphism). If, in reading this article, you wonder what about those times when you have an object with 12 parameters that is instantiated inside some other object... ask yourself why you're doing this first, and don't just answer with "Because it was the simplest, easiest thing to do." Simple is not equivalent to the least amount of work. Think towards root cause analysis and solve, or at least move towards solving, the root cause.
 
In this case, IoC containers never solved the root cause of why my objects were so difficult to instantiate and use. IoC containers never helped me to make a system that better communicated my intent. IoC containers did however help me create a composite application. So I will continue using them to that aim and cease using them for most others.
 
Good luck with this! Tell me your thoughts and if you think I'm stark raving mad by leaving a comment or shooting me an email. Thanks!
23 responses
It's not a bad idea when you've got only a few dependencies like in your code. I've worked with the same concept and it's really not that bad. But once you cross into hundreds or even thousands of dependencies, you're going to be unmaintainable. You'll end up with too many lines of code just for setting up dependencies that could just as easily have been done with a single line of code in a IoC container (if we're talking Injection alone).

You're right though, containers make it just so easy to toss in as many dependencies as you want without regard to whether or not it makes sense for them to be there. When you use one, you've actively got to be aware of this.

In short, I've got projects I work on where this works and works great, but I have other (much) larger ones where I wouldn't touch it with a 10 foot pole.

Your IOC wire-up example shows that you are expecting the container to manage lifecycle (some are marked as singletons...), but you dont show any equivalent when doing it by hand. Are you comparing apples to apples? Is there a bunch of hand rolled singleton code that are leaving out in the non-IOC version?
Also, the latest releases of StructureMap use a much terser syntax:
x.For<iserviceprovider>().Use<serviceprovider>()
mendicant:
IoC containers would not do any better at managing thousands of dependencies while delivering the value I'm looking for either. You're just moving the pain... and in a way that is less clear. If you're speaking to whether or not these ideas could be used on a larger project, they can. If you think they can not, I would love to see some code so that I can help you out. :)

Joshua:
It is apples to apples, the singleton was a possibly unnecessary way to ensure that all instances created used the the same instance of that object. Also, I don't think the IoC code is the least bit heinous, it's just doesn't deliver anywhere near the value the wired up code does. StructureMap is a great tool that is misapplied too often.

Would you considering going as far as applying this approach when you are working with a framework that is already very IoC friendly such as ASP.NET MVC? Maybe I'm just lazy but I like how I can drop in a container and let it build my controllers (and their dependencies). Furthermore, while I can appreciate that in the example you've provided there's some useful information that can be gleaned from reading your startup code, I can't say the same thing for the application I'm working on at the moment - there are some controllers that talk to some services that talk to some repositories and thats about it. You wouldn't gain much from stating it explicitly.
I'm curious how you would approach lazy instantiation. It seems using this concept would lead to a heavy memory footprint and slow start up having all objects instantiated at app start. There would also be a lot of objects sitting around doing nothing. Does this not get in the way of the GC?
Please correct me if I am wrong but you seem to be saying that, in general, this is a better, cleaner and clearer approach than wiring with IoC.

However, where have you considered the full set of features most advanced containers support? Convention over configuration, auto-wiring of common dependencies, AOP via interceptors, lazy loading...

Don't get me wrong I completely appreciate the simplicity and cleanliness of your specific example, but on it's own it doesn't convince me to give up IoC. It might however convince the uninitiated not to try/learn IoC and in that respect I think you may be doing a disservice.

Paul: I need to see your code before I could make a specific recommendation. Is it simplistic enough you could quickly throw something together on pastebin or gist.github.com?

NotMyself: I haven't had that happen, nor have I been in danger of that happening. It's important to note that I'm not constructing value objects here. Those are an entirely different use of objects.

Chris: Our community needs more discussions of controversial topics not less. It's silly to talk about people reading a single blog post and just not trying. The people I've met in this community, both new and old to it, have all been excellent critical thinkers. The disservice would be in not expecting people to be able to make smart decisions on their own.

Having said that, what value have those features given you? Have they been about productivity, business value, living documentation, improved testability... etc? Let's talk specifics here.

I don't think your use of the word IoC is as it is conventionally understood and practiced in enterprise computing e.g. Java containers. You're tying a lot of your code to a single application. It is also not immediately clear how many different application configurations you have across all servers and problem domains. With IoC, that becomes deterministic providing everyone is using the same software configuration management repository. In other words, I think the biggest problem with your original piece of code is the fact all your code was stuffed in App.xaml.cs code-behind, and your problem domain had no control over what the application was/is.

As for people getting started quickly on how a system works, read Zed Shaw's advice to Ruby on Rails creators when he first tried out Rails. Everything is dead-on accurate, and how I wish everyone would do it.

I've thought this for a while now after using several IoC containers in Java-land.

Sure they provide advanced features, such as transactional demarcation, lazy-loading, etc, but 90% of the their use is something that can be accomplished a lot more sanely using good old code.

for larger projects you just do what you do with any decent IoC container - you split it up into modules. Hopefully the dependencies are naturally grouped by function anyway, providing you with the larger building blocks of your code base, so it should naturally follow that you have separate 'module' classes that group those wirings together.

I like how you link to your container declaration, which has no details, then you re-write a bootstrapper and compare Apples to raisins saying "The problem I have that needs solving is how to make my code into living documentation that describes itself long after I've forgotten about it."

Dude, your bootstrapper, the living docs are right around the corner.

http://github.com/jcbozonier/alloy/blob/086e928734359b573df99edf8121f79c6aded...

----

"Have they been about productivity, business value, living documentation, improved testability... etc? Let's talk specifics here."

Productivity: IoC lets me easily create nested chains of functionality without having to worry about service location way down deep in my Logger. With conventions I don't even have to touch the global.asax or my configurations. Code and go.

Business Value: Lifetime management is right around the corner. huge value in saying "this needs to be a singleton and saying ok, 10 seconds for a fix. Thats another huge productivity gain.

With web stuff your constantly dealing with lifetimes. I don't think your example would even fly in the web world. Am I putting all those classes in session? Geez.

Living Docs: In the new StructureMap the syntax even more readable. And I see no benefit in having a huge mess of nested constructor calls. I would disagree a new programmer would gain much from your technique. They still need to know what the point of the code is. "CopyToUrlService" What does that do?

Testability: One container for each environment. Switching is on the fly. How much easier can that be?

Justin:

My experience relies on the fact that in 99% of circumstances, an IoC can be autowired such that a dependency of IService is resolved by an actual implementation of Service. (Heck, if you want you can even eliminate the interface and just have an implementation, that's not the point though).

So now, let's say I'm building a new feature, I could A) Go into App_Startup, ensure that I'm creating my screen below my dependencies, create a new screen, pass it my dependencies and be done. Or B) with a nicely autowiring IoC, do nothing.

Now, let's say a business requirement or refactoring has introduced a new dependency into my screen. I could A) Create the new dependency in my App_Startup, making sure that it appears before my screen and add it to my screen's constructor. Or B) Use an autowiring IoC container and it do nothing, because so long as I follow convention, it just works.

Now, let's say I'm working on a team of 4 pairs, where each pair is working on a new screen. Each team adds a new screen, and by default developer habit, puts that screen into the last line of App_Startup. Let's be simple and say that all the dependencies for the new screens already exist. Still, first one to checkin wins, because after that the next four pairs are all going to get screwed (not for sure, but looking at your code likely) by collisions which they will have to resolve before checking in. Or.... if they were using an Autowiring container... they could've just done nothing.

Granted, these are all fairly trivial and not difficult to solve, but at the same time, it adds up.

My main point is, before you take into account more advanced scenarios like lifestyle, etc. You can probably take your entire 14 lines of wiring up code and make it 1, and that 1 line of code is far easier to manage whether it's 14 different dependencies or thousands. The only difference is that the pain of doing things wrong with an IoC is a lot weaker and you've got to be paying attention to notice it.

If all you're worried about is documenting the way your dependencies are created, and you think that the IoC initialization code is too unreadable for that, why not just write a few convenience methods that hide the complexity of the IoC syntax? That seems like a better solution than short-circuiting all the benefits of an IoC container.

There are two things that I would not be able to accomplish, were I to switch over to this method:
1. Unit testing,
2. Windows Services

To debug a Windows Service, you have to put the service code into a class lib and use a test harness to launch it for debugging, and another wrapper to launch it in production. The harness needs different dependencies than the production wrapper.

Also, by putting your dependency-creation code into an IoC, it's all in one place - for all the consumers of those classes - seems to me that is better documentation of the application as a whole.

Justin,
Innocent qn:
Isnt it better to hide implementations of sub sytems behind their packages or interfaces.

Do we even need to expose this wiring up front.

The original implementaiton (prior to you breaking wiring up) was also using a lot of lookup logic. So I am confused where IOc is doing the DI.

Also, what is the benefit of exposing the wiring up front. I am not clear.

Thanks
Sridhar

I am not ready to give up my IoC just yet. I am with John Farrell when he speak of productivity:

I am kind of forced to use Unity which lack the fluent configuration StructureMap provides. At first I hated it for that reason, but that forced me to rethink my conventions. I have my domain code in one assembly and the implementation, the infrastructure in another. All I have to to is to register all infrastructure classes which implements interfaces from external assemblies.

Whenever I need to refactor or reuse a dependency, all I have to do is to add another constructor parameter and I am ready to go.

Besides, without a dependency container, it would be next to impossible to partition my that way, domain code in one assembly, infrastructure in another.

To John (and everyone else who's taken the time to comment):
This is part of a larger whole and based on what I value as a developer who's primary concern is making my business as successful as possible.
We as developers tend to place too much of a cost on typing and too little on communication/discovery of our problem domain. which speaks to a certain irony given that the risk to most software development projects seems to be related to unknowns making estimates wildly varied and NOT because we knew everything but just couldn't type fast enough, there's a certain irony here.  Ultimately, I plan to show that code can become much more communicative, less error prone, and thus more predictable and dependable as a result.
This article is about codifying the structure and general behavior of a sub-system then leveraging this understanding to flesh out the missing concepts. In a word, cohesion.
The next article will discuss risk mitigation, extreme flexibility, and testability from the perspective of object oriented design.
Once that ground work is laid I will get into a bit more depth and perhaps get a little more abstract.

I love this statement...

"We as developers tend to place too much of a cost on typing and too little on communication/discovery of our problem domain"

I've often said that the hardest part of programming is not the typing

I think your reply is a major punt an attempt to spin a bad argument into a different topic.

IoC's are superb for communication and discovery of an applications functionality and have the benefit of offering added bonuses like lifecycle management and no-hassle injection throughout your object graphs.

Justin,

Listen to John Farrell. He is giving you good advice. It is a shame he doesn't have an active blog! Really, in Java, the biggest annoyances with IoC containers were: (a) XML baling wire that could not be easily debugged (b) Java's lack of support for higher order functions and functions as first-class values

For (a), the landscape changed when Google released Guice. Now most IoC configuration is done in code and not XML. It is even better in .NET because generics do not have the JVM type erasure backwards compatibility disaster.
For (b), with higher order functions you can quickly see component dependencies and what object is ultimately being returned. Object-functional languages like F# make this especially obvious due to how F# elegantly models objects as simply instantiable modules. Java programmers had to suffer with inner classes.

In F#, you can combine higher order functions with pattern matching and partial application to produce truly modular, combinatorial application definitions. With an IoC container and C#, you can almost approximate the same value F# gives you out-of-the-box.

IoC containers can also have other benefits, such as automatically performing steps required after object construction. For example, in WPF, because of bad API design decisions, in order to sign an ImageSource a BitmapImage object you have to (1) instantiate the BitmapImage (2) create a new Image object (3) assign the Bitmap you just created to the Image object's ImageSource property (4) call Freeze on the Image, so that the Image object does not leak memory if you later change the ImageSource (5) Pass the Image dependency to your application so that it can wire it to the WPF Control that requested it as a dependency.

Now that I've centralized this wiring code in an IoC container, I can use static analysis tools like Mono.Gendarme and FxCop to write rules that check to see if other developers are writing code without calling Freeze, because all of that information is right there in the IoC constructor call graph, fully statically analyzable. On the other hand, you think your code is now self-documenting, whereas I see it as potential for an ad-hoc mess where no coding standards can be enforced across the project. It smells of One Man Gang small software shop Mickey Mouse club programming. With your approach, you are doing all that WPF wiring I described above by hand. Also, when you have to port your code to Silverlight, you are now going to litter your code if #ifdef's, because Silverlight doesn't have an Image.Freeze method because there is no such concept as Freezable in Silverlight, nor is there a BitmapImage. Think about that; BitmapImage is purely a WPF-specific rendering optimization for putting a picture on a control (and not even a particularly good optimization). With IoC, my Application doesn't even know about that optimization. WPF just renders whatever my Application gives it from the IoC module.

In addition, my Application class is no longer directly dependent on my module dedicated to providing it with its dependencies. I can update the IoC module without touching my Application class. This makes it wasy easier for me to predict to my boss how long something will take.

Nope. These discussions are extremely difficult to come to a good conclusion in person let alone over a comment thread. All I can offer is that in the environment I'm in and with the pet projects I can share with you this works. I would love to see some code from you that illustrates your opinions.

I brought up soon to come articles because this is a lot like extreme programming practices such as pairing, TDD, and rapid iterations, done, etc. Taken as a whole the practices work off of each other extremely well but are difficult to discuss at any length without writing a book, but individually it is much harder to really describe the value while being much easier to discuss in bite size amounts.
This approach to wiring up actually originated from a solution to a completely different problem and the way these different ideas support each other in philosophy and practice shows me I'm on a good track.
I'll offer you this though: I'm sincerely glad that you don't agree with me if for no other reason that it helps me to better flesh out my ideas. Are you in the Seattle area or even available via IM? I would love to pair with you on some of this and figure out where our ideas align.
Sridhar, where the wiring goes wasn't so much of my point as much as keeping it together and using it to flesh out your system.
In my experience, IoC containers are a social thing. Java developers are expected to know what they are on an interview. .NET developers generally aren't. The knowledge set for Java development is pickier because Java was on the Web first and open source Java frameworks were always ahead of .NET.

Why do you think your code is more literate? As somebody who is building a literate programming system, I was somewhat shocked to see you call your system an example of literacy. Fundamentally, you are hardwiring sequences of collaborations. To upgrade your app, you must upgrade the entire system. There are deep tentacles into your system through concrete dependencies. This makes it harder to unit test. I am not sure how you can equate this to XP. One of the biggest process-related issues with XP is the idea of Continuous Integration. With your set-up, you cannot have CI. It will also be really painful for others to merge in changes. In a real-world project with many contributors, you want to make merging easy, otherwise the maintainer of the merge tree will burn out. By breaking your program into modules, you also make it less likely you'll introduce dependencies.

I don't live in Seattle. It rains too much there. I want to live in a place that has SEASONS.

Dont forget that lazy instantiation/evaluation is just an optimization and perhaps premature
Kind of agree with John's first paragraph. I'm a .Net person, was interested in IoC containers but never really had much use for them. Every time I tried, I always went back to hardwiring them like you, and also like you thought IoC was much ado about nothing. I think a lot of the discrepancy might be because .Net is most often desktop apps (smaller, fewer dependencies) and java is more often server-side.

However, my most recent project presented an aha moment to me--I (once again) explored doing IoC, got everything set up, and then got a new requirement to log things at startup. The startup class didn't have the IFileManager dependency, or the ILogger dependency, or the ITimeProvider dependency, nor did some of the other classes it managed. With Autofac, all I had to do was add IFileManager, ILogger, and ITimeProvider to the constructors of each of these classes, and write the logging code. Autofac took care of the wiring automatically. Then when I realized I didn't really need to pass IFileManager or ITimeProvider to each of these classes but could instead just add IFileManager and ITimeProvider to the StandardLogger class constructor, I didn't have to make any configuration changes there either; Autofac just did the magic itself. Then when I wanted to add an abstract post-save-process interface to a class way down in the dependency tree, I didn't have to create an IPostSaveProcessFactory and pass it all the way from the top down to the bottom; I just added IPostSaveProcess to the constructor and declared the CustomPostSaveProcess in the Autofac initializer.

This all came in especially handy when I was working on different devices. Sometimes I'm running on real embedded hardware, sometimes I'm running on my desktop connected via USB to our devices, and sometimes I'm just testing stuff out on my home PC against mock devices. Those would each have completely different procedures for wiring up because the concrete classes have all different dependencies and orders-of-dependencies. Of course that could be worked out, but with Autofac all I have to do is say "if hardware register RealPostSaveProcess, RealFileManager, etc, else if laptop register NullPostSaveProcess, LaptopFileManager, etc", and Autofac wires up all the dependencies itself.

After a couple weeks of working this way, I have to say your view (and my old view) on IoC containers is similar to the old C++ programmers' view on garbage collection. Sure you can do it yourself. Sure you get more control that way. But with so many tools that can do it for you automatically, and do it probably better than you could do without lots of planning, why would you do it yourself? Same thing with C versus assembly--no matter how good an assembly programmer you are, almost always a good C compiler will beat your hand-written assembly hands-down.