My favorite patterns: Injected Configuration

Dave
5 min readNov 28, 2018
Photo by Mpho Mojapelo on Unsplash

When a developer gains experience, they will often gain a “sixth sense” about code. However, intuition is an inexact thing. As we’re getting feedback from our experience, it’s important to properly contextualize it so we are learning the proper lessons.

Because “the repository pattern sucks” actually means something different than “I had a bad experience with the repository pattern because its constraints were a bad fit for what I was doing at the time.”

So I’m hoping to write a few quick pieces on some of the patterns I like (and maybe some on patterns I hate or have found limits to). First is one that I really like for server-based services.

The problem

So I made up the pattern name, “Injected Configuration.” Why has nobody mentioned it before? Probably because those who write pattern books consider it too obvious or something. But I think they should reconsider, because the vast majority of the software I’ve seen does a lot of this, even deep, deep down the dependency tree :

// pseudocode alert!
var connectionString = ConfigurationManager.GetConfig("connString");
client.Connect(connectionString);

Any .NET developer should be familiar with this kind of thing. This is all well and good, and works like a charm, but there are a few issues:

  1. It’s not very testable. Your unit tests either need to have a config file with a value in there, or a way to override that static class.
  2. The dependency is hidden. Unless you see that line of code, you don’t realize that the class or module depends on some file being on disk.
  3. What if it’s misconfigured, or missing from the config file? You won’t know until you hit this line of code and see failure.

The pattern

So what do we do? The solution is dependent on another pattern, inversion of control. Ideally your web application or service, if it has more than one class dependency, uses something like constructor injection, perhaps along with a DI container, so each class that has dependencies has them passed into it upon creation.

The key idea of this pattern is to treat configuration as a dependency.

Just to be clear, I didn’t invent this idea. In fact, ASP.NET Core encodes some aspect of this idea in its APIs, calling it the Options Pattern. It really follows naturally from the principles that animate practices like IoC and TDD.

Let’s look at some code. Imagine the following class and method:

Now, you passed in your email provider, so that’s something that you can test. The dependency is visible, as it’s right there in the constructor. Very nice. But what about that ConfigManager call? Well, one obvious improvement is to pass in the ConfigManager as an interface. But let’s take this to the next level. Why not just pass in the configuration itself?

You’ll notice a couple nice things. First, of course, the dependency on the configuration is explicit. Second, it’s strongly typed, which makes it easier to consume and prevents a whole class of errors. For example, fromAddress is a string, and the tooling will make that clear.

The object itself is simple, and might look like this:

Of course, the real benefits come in when we want to make sure the configuration is valid. So we probably want something like this:

How many times have you had your app running in a development environment just fine, but then it blows up in production because a config value is missing? Or worse, it doesn’t blow up, it just doesn’t work properly and nobody notices until 600 customers haven’t gotten their emails??? I, for one, have been there, and would much prefer to know sooner than later.

In the spirit of failing fast (see the excellent Release It! by Mike Nygard on this point), you probably don’t even want your application process to start successfully if the configuration is invalid in the target environment. Ideally this means your deployment/release fails gracefully and the users don’t even notice.

The key point here is that your application depends on a set of valid configuration data to behave properly. If our DTOs to hold data coming out of the database are strongly-typed and unit tested, why shouldn’t our configuration classes be?

And yes, I have written unit tests against configuration classes before.

If our DTOs to hold data coming out of the database are strongly-typed and unit tested, why shouldn’t our configuration classes be?

You probably have a set of key/value string pairs, either in an XML config file, or a JSON file, or stored securely by your cloud provider, and you need a simple function: keyValuePairs -> ApplicationConfig where some values need to be checked for valid values (e.g. valid address, can be parsed into a boolean, etc.), and some combinations of missing values is fine, but others are not.

If you’re using a DI container, that’s certainly a nice way to make sure it gets into all the classes that need it.

So that’s it. Treat configuration as a dependency, make it strongly typed, unit-test the code that transforms it from its state at rest, and inject it where it’s needed. Your code is more testable and less likely to be missing necessary configuration at runtime.

Some recommendations for further reading:

Update (2019–04–23)

A reader was kind enough to point out an unfortunate omission in my description of this pattern. If you have an app is big enough to be non-trivial, you likely don’t want to have a single class for all the configuration in the application.

Instead, use multiple classes to get the appropriate separation of concerns between various parts of your system. Above I linked to Microsoft’s .NET Core's use of what they call the Options Pattern to support configuration, which enshrines this idea, which is just a special case of separation of concerns, or the Interface Segregation Principle.

Suffice it to say, it’s always a good idea to avoid coupling different parts of your system together if they won’t change together, or are not part of intersecting use cases.

So thanks to that reader for the correction! And no, don’t use a single application-wide class for all configuration.

--

--