If you’re writing SOLID code, you’re going to be doing a lot of wiring. Typically, this wiring only changes in response to business change. Some wiring is going to be static for years. If you’re building a car, you won’t suddenly wire the clutch to the CD player, as entertaining as it would be when your brother-in-law tried to drive it.
On the other end of this spectrum, there’s stuff that changes all the time. Every machine you use, it needs to change. This is actual configuration information. The rest of it can, frankly, be hard wired. True configuration information needs to be in external files, wiring is best left in C#. Take a look at your config settings. How much of that has changed in the last year? Two years? Would it really be a big deal to create a new version and deploy it in the unlikely event that you needed to change it in the next two years?
Configuration API Design
It may sound like I’m just stating the obvious here, but it’s try to think of a major .NET project that gets this. I’m reckoning it’s nServiceBus and… well, no-one actually. Microsoft definitely don’t get this or your average config file would be five lines long, rather than 200.
Castle Windsor is schizophrenic on this. There’s an XML configuration model in which everything is configuration, and a fluent model in which everything is wiring. What you actually need is environmental deltas like Binsor has. Now, I have form on this subject, having been blogging about configuration management and the need for derived containers almost as long as I’ve been writing.
So, does everyone need an XML format as well as a C# API? Well, no, because you can use a braindead DSL these days. You don’t need to wait for Craig and Oren to write a Binsor equivalent for whatever library you’re using. Moreover, although I’ve made a big distinction between wiring and configuration, the difference between the two is going to be application and domain specific. What’s consistent for one company could change on every box at another. It’s best to be using the exact same API as you are in C#. It’s easier to learn and it doesn’t impose an unnecessary barrier between the stuff you change and the stuff you don’t.
Now, after I’ve said all of that, it might be surprising that I use Binsor. Shouldn’t I be using Castle’s fluent registration? Honestly, I’d much rather use it, but Binsor has the Extend macro, and Fluent Windsor doesn’t. The Extend macro enables my separation of configuration and wiring in a way that Windsor’s fluent interface does not. If you dig deeper, you discover that the fundamental problem is you can’t have incremental registrations.
(As an aside, this is because the container violates SRP extremely badly, being responsible both for registration and for resolution. This, in turn, prevents the creation of a registration compilation step and introduces all sorts of problems that are most easily solved by treating registrations as immutable.)
Achieving Configuration Nirvana
So, here’s how I think we should be thinking about configuration:
- Configuration is different from wiring
- The difference between the two is application specific.
- Configuration needs to be in files external to your code. This doesn’t, however, mean you need two mechanisms.
- There’s no need to use XML. IronPython, IronRuby or Boo are fine.
- Specialized DSLs are not the way to go. There’s nothing wrong with .NET code.
- Configuration APIs need to be incremental to support this. If this means having a separate “configuration compilation” step, so be it.
- Most libraries shouldn’t concern themselves with configuration. IoC containers are fine.
I haven’t really explained the last point. We tend to behave as if configuration is something we need to build into everything we do and this is not only wasted effort on the producers’s part, it’s often a pain in the neck for the consumer as well. I’ll talk about that some more on my next post. Then I’ll come down to earth and start discussing what we do in the real world where my perfect configuration system doesn’t exist.