App configs tend to get bloated with all kinds of configuration settings that are not likely to change in production. Those huge files are a pain to read and it's easy to get something wrong causing the whole app to fail. Another thing to take into account is that everything in the config file need to be well documented so that the sysadmins know what to change and when.

So what am I getting at?

Think twice about what you put in you app.config. A lot of things can and should be configured in code instead. You as a developer are in charge of the source code and all decisions that are yours or things that shouldn't be modified in production should go there. This results in configurations that are:
• More readable to both developers and sysadmins
• Easier to modify when the need arise
• Easier to document for developers
• Only contains setting that are eligible for modification in production

What should go in the app.config then?

I found it useful to ask myself the following question when deciding what to make configurable:

Is this something that our sysadmin should be allowed to change when the system is running in production?

If you can answer the above question with a yes then it's most likely a good candidate for your app.config. If not, put in in the code!

Logging - an example

Let's using logging as an example on how to apply this rule. Using log4net logging is usually configured by the following syntax in the app.config

<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="whatever.log">
</file><appendtofile value="true">
</appendtofile><rollingstyle value="Composite">
</rollingstyle><datepattern value="yyyyMMdd">
</datepattern><maxsizerollbackups value="100">
</maxsizerollbackups><maximumfilesize value="10MB">
<layout type="log4net.Layout.PatternLayout">
<conversionpattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline">

<level value="DEBUG">
</level><appender-ref ref="RollingLogFileAppender">

As you can see we allow configuration of:

  1. What appender to use, a rolling file appender in this case
  2. Where the log output should be stored
  3. That the appender should create a new file for each 10 MB of logs
  4. That the appender should keep the most recent 100 files
  5. The logging threshold
  6. The layout if each row in the log output

Applying the control question to the above statements reveals that the only thing that make sense to change on the fly in production is 2 and 5.

You might argue that it's good to allow for adding new appenders without redeploying code. But come on, how often do you get in a situation where you need to change that on short notice in a production environment? Your CM procedures should allow you to patch and redeploy quickly anyway.

What about settings that should go into the code that might vary between the different environments during development, test and production? This should be handled by making your application lifecycle aware.

By configuring logging in code you can make sure that only the relevant settings are exposed to your sysadmin. By doing this we end up with the following config. Much cleaner don't you think?

<add key="Logging.Threshold" value="INFO"/>
<add key="Logging.OutputDirectory" value="c:/logs"/>

The icing on the cake
Let's take this approach on step further by applying some convention over configuration in order to totally remove the need for logging configuration for normal production operations. This is easily done by defaulting the threshold to WARN and the output directory to either the current working directory or ./logs. If the admin doesn't like this he can always override as he likes. This gives us the following of benefits

  • Forgetting to configure logging will result in the default settings beeing used.
  • No need to have your deployment scripts modify the settings when you move between environments
  • Less risk for misconfiguration
  • Cleaner and more readable config files

So next time you add something to you config file consider configuring it in code instead!