Where to put application configuration?
What is a good approach to configuration? I feel that often we tend to pile up everything that looks like configuration into (one big) global config file which is then used from everywhere in our application code. In the following post I will look at some reasons why keeping configuration outside application code may make sense and also why we should do it only when really necessary.
There are many ways how to store configuration outside of the application logic. However, for the purpose of this analysis it does not really matter if we use special config file(s), store config in database or OS environment variables.
Here is an example of file based config that lists supported currency routes:
These config files are typically placed under separate root directory than the rest of application code. For example, in case of Java they are typically under
Alternatively if we decide not to extenalize this configuration we can write something like this:
The forces at play are:
Who needs to make changes?
If non devs need to make changes then it might be valuable to use some simpler format like yaml or properties file.
How often do we need to make changes?
If configuration changes are happening very often then we might want to bypass the deployment process for application code and build some kind of UI that allows direct access to config in production.
Is this configuration different per environment?
Most application frameworks support some kind of notion of environments and provide easy way how to define environment specific stuff in a separate block within one configuration file or in a separate file.
If we need to make sure that configuration values are not accessible for everybody e.g database access credentials for production. In some cases the opposite might be true as well — we may not want to give people outside the team permissions to deploy the app but we may allow them to make changes to certain configuration parameters.
How quickly we need to make changes
In some cases we may not want to wait until new application package gets through the CI pipeline and is deployed.
Why not then?
In short if none of the above reasons apply then we most likely should just hardcode configuration as part of our application logic. In addition there are the following reasons why we should avoid externalizing configuration:
Even if we try to use very self-explanatory names for our configuration parameters then often to fully understand how they are used we still need to look up the code that actually does something with this configuration. If we keep configuration and code closer together it is easier to do.
Another problem with text based config files is that they tend to become quite big. Of course we can split them up and even try to mimic the structure of the modules that are using them but that requires discipline to keep in sync.
It is quite likely that the there is tight coupling between the code and parameter names in configuration. If we change one without updating the other then we will probably break something. Most likely we want to keep things that are coupled close together.
If application is big enough we may very soon find that many different modules are using some of the config params that were supposed to be private to one specific module. We have effectively broken the encapsulation of that module and are now exposing some of the internals to the whole application. Of course we can try to alleviate that by having some kind of namespaces in configuration but that is still only a convention that may be hard to enforce over time.
Reusing deployment process
This applies when we are using some special admin UI or other tools for directly editing our configuration in production. Yes, it may make it easier for non devs to change the values but we will be missing all the rigor of regular development pipeline and we need to build our own auditing/reviewing tools. Often a change in application configuration (e.g switching feature toggle) may alter application behavior as significantly as regular code deployment. This means that in terms of risk we should treat config change just like any code change.
When we have some text based config then it can be quite hard to even find out what kind of configuration possibilities are supported. The only way to find if a change is valid is starting the whole app. If config is in code then we can make it strongly typed and let IDE help us figure out what is valid and what not.
Additional complexity for accessing external config
Frameworks like Spring make it very easy to inject configuration into global singletons (Services). However, it is much harder to make this config accessible for Entities and Value Objects. This may push whole application design more towards procedural Transaction Scripts with Anemic Domain Model as most of the behavior will need something injected from our configuration.
I think externalizing all what looks like a configuration is probably not a good default. Instead we should start by keeping the parameters close to the code that uses it. Maybe start with private constants and then extract into separate configuration objects if we see need for that. Using text based configuration files or other ways of storing config outside of application code should be used when it is clearly needed for given scenario.