What is configuration?
Programs can be configured. When you talk to people about program configuration, they often think of things like what theme the application uses, the locale formatting, wether you want notifications turned on or not, etc.
This is user configuration, not application configuration set by a developer. These are different kinds of configuration. In this blog post I'll explain three types of configuration and what sets them apart.
Types of configuration
I might do a tree with a taxonomy of configuration categories in a later post. But for now, let's keep it high-level, we stick to three kinds of configuration.
We start with user configuration: this configuration is scoped to a user, does not affect the experience of other users. You will not find this configuration in any deployment package, except maybe for the defaults required when a new user is created. This kind of configuration lives in the database or in userspace (or if you want to be hip, the edge).
Secondly there is application configuration: scoped to the application, shared between all users. These are often things like timers. The application will often have to parse the content of this configuration to affect control flow or setup things.
Thirdly there is environment configuration: scoped to the environment. These are things the application needs to become part of the environment it is running in. Applications don't care for the contents, they just send the bytes to somewhere else in the environment. These are tokens, passwords, hostnames.
Example of configuration
Let's go through a few examples to help decide what kind of configuration we are dealing with by using a simple YAML example:
application: database: hostname: backend_db.local username: guest password: guest user_table: users session_store: file default_timezone: UTC default_theme: light admin_theme: dark
Now for each, let's go into depth on what kind of configuration it is and why:
database.hostname is environment configuration: where should the application look for a database? This is a configuration that tells the application something about the environment it is in. The same holds true for
database.password. Another thing that shows you it's environment configuration is that the application could not care less about the content of these fields: hostname is passed to the operating system, username and password to the database. The application could not care less if the password is long or short, it just takes it and does not inspect it.
user_table is never really inspected by the application either, it to is an environment configuration parameter.
session_store. This is application configuration because it configures the way the application behaves in the environment. This configuration wil change the way the application behaves.
default_timezone might look like something that connected to the environment, but like
session_store it tells the application how to behave, making this an application parameter.
Lastly we have
admin_theme. The first is application configuration, because it represents the default theme presented when no other value is known. The second is interesting: it's the theme of the admin. If the admin is allowed to change the configuration and that configuration change would be stored in this config file, then it would be user configuration. If the setting is immutable, it's application configuration.
Where should each type of configuration live
Application configuration should be shipped with the application. It's part of the core logic of the app and could also be considered code. You should have this shipped as part of your application because it needs to be tested as part of your application. Testing is also the reason you should keep this configuration simple. Every feature toggle will double the testing effort you need to keep a stable having a stable application. If you want to toggle application configuration from outside of the application, make sure you keep it very simple. For example, allow a set of feature toggles to be selected (preview release configuration and stable configuration) but do not allow each feature to be toggled individually.
If you have fast continuous deployment, you could have most of the environment config be part of the application config. This leans towards the configuration as code approach, where configuration is deployed and versioned like any other application. You could also see this as a reason to ship it with the application. A reason not to ship it with the application, is because it has a different life-cycle. It may have to be updated between deployments. For example, if you want to migrate the database to somewhere else, or rotate credentials. You should be able to do so without having to role a new release.
For secrets, take inspiration from solutions like Hashicorp Vault: every secret is unique and you have to actively refresh it and collect it. And if it's not a secret: ship a default and allow the configuration to be overridden via, for example, environment variables. Make sure you keep your developers know about the configuration and keep the overrides to a minimal.
I would advice against the complexity and single point of failure of a centralized configuration server like Spring cloud config. If people start pushing for this, start saying things like "if configuration is easier to change in prod than code, make your code easier to change in prod.".
Lastly there is user configuration. This is kept inside the environment it's the user accessed the application in and should be considered private data until proven otherwise. Dealing with how to flow private data back to the development environment is a topic for another post.