With DevOps bringing source control to configuration files and publishing to production servers being automated – bringing both code and configuration over on the same time, the difference between code and config has become less than ever (if it even exists).
A few weeks ago I reread Mike Hadlow’s brilliant post The Configuration Complexity Clock. As I’m also in the middle of setting up publishing routines for a web application I started to think about the difference between configuration and code. The more I think about it, the less clear the difference is.
A simple attempt to differentiate them would be to look at where they are stored.
- Everything in source files that are consumed by the compiler is code.
- Everything in configuration files that are read at runtime is configuration.
Unfortunately that is not the entire truth. For example the route config in an ASP.NET MVC application is a kind of configuration – but it is done in code. The same is true for the Entity Framework Code First mappings – it is done in code (either through attributes or a fluent API), but is a kind of mapping configuration. An example of the other way around is startup configuration scripts (think of *nix .bashrc
or the old autoexec.bat
on DOS systems). It is configuration, but is run as a script.
There are definitely cases where it is not that simple to define what is configuration and what is code.
And does it really matter?
For web applications that are deployed in single copies I’d say that it doesn’t. With DevOps becoming stronger and introducing proper handling (read: version control) for configuration files both config and code are handled in the same way. They are both handled in a source control repository, subject to code review practices and uploaded to the server through an automated publish pipeline.
With that kind of process (which in my opinion is mandatory for any professional shop), there is no longer any cowboy style config updates on live environments. Both config and code are updated in exactly the same way through the same process.
Using Code or Config
That leads to the next question: What’s proper to put where?
I might be a bit radical, but here’s my take on it (that I haven’t yet implemented in any project):
Put anything you can into code that is checked at compile time.
That doesn’t mean that values should be hard coded all over the place. That’s not DRY. Put the values in a well known location that can be accessed through a simple to use façade. If you later find out that you want to move some stuff from being compile time constants to be stored in a config file, it is easy to do that by modifying the façade.
Difference Between Environments
The one case where I think that it makes sense to use config files is for values that are different in different environments. For anything where your development and production environment should differ, or where the production and the user test environments should be different you have to use config files. You don’t want the code littered with conditionally compiled stuff. The compiled code deployed to different environments should be identical (except for optimization levels for debug/release builds).
The ASP.NET way to deal with different configurations for different environments is through config files transforms, which is really powerful and simple to use on the same time. It finally got the configuration hell of all our different environments under control. (Some stuff can also be changed in the publish process, but I prefer using config transforms as they are more powerful and easier to persist in source control.)
Flamebait
First thing I’ll do tomorrow when I get to the office is to start moving things out of web.config
and into configuration classes in the code.
What do you think – is that the right thing to do, or are there other pros with config files that I have missed? Please leave a comment and explain why I’m wrong to get the discussion going!
I agree with the concept that all things that changes state of an environment should be tracked by an SCM. I also subscribe to the notion that “everything is code”. If it alters a solutions behavior, I will consider it code.
However, placing all configuration in compiled code is not necessarily my preference. For a small project where all project members are capable and supposed to change and administer all “code” it ought to work well. In a large environment every tier (and often also different technologies in the same tier) has different experts managing its configuration. Then it is not necessarily optimal to stick all configuration into the language of choice for the developers.
I imagine the linux/unix admins would dislike having all their /etc files managed and deployed through .Net code. I do not think DBAs would want to have their database configured via Java/C#. The same goes for web and app server admins, build managers and so forth.
Yes, everything is code…
If we however view this in context of code that configures a specific solution, then of course is critical to version control all config together with the code and quite possibly in the code.
As I stated on twitter, this is all about scale. The smaller the solution the more is feasible to have in code. The larger it is, the more we have to cater for the preferred tool of the different professions that will be involved in managing to solution in production. All “code” should of course still be managed in the same version VCS.
Just my opinion, and we all know what they say about opinions… :-)
I agree with you, but transformations are of limited use IMO.
For instance, I have usually 3 envs: Devel, Production and Test.
However, I have number of testing systems – one for each feature branch. Also, if you have load balancing you have more production servers so that defeats the purpose of some parts of transformations
Currently I have post build job that changes my Test.config. Parts of the app.settings are defined as properties of branch root folder (SVN, for instance background color of the web app so people don’t accidentally try something on wrong place).
So I think configs are of limited use, and powershell scripts should be used as pre/post build event for precise configuration. But I moved all conditional compiles to app.settings as conditional compiles suck big time IMO (for instance it was typical for me to commit some changes that fix problem ONLY in one of the conditional parts of the code … ). I use conditional compiles only to set up some default values for developoment branch in order to speed up testing.