The authors of the manifesto of 12 factors spoke on this topic.
Here is what they write about the configuration :
The litmus test of whether the configuration and application code are properly separated is the fact that the application code base can be freely available at any time without compromising any private data.
Further:
Another approach to configuration is to use configuration files that are not saved to the version control system, for example, config/database.yml in Rails. This is a huge improvement before using constants that are saved in the code, but there are still some disadvantages to this method: it is easy to save the configuration file by mistake to the repository; There is a tendency when configuration files are scattered in different places and in different formats, because of this it becomes difficult to view and manage all settings in one place.
This is the way described in the question. The authors of the manifesto offer this solution:
An appendix of twelve factors stores configuration in environment variables (often abbreviated to env vars or env). Environment variables are easy to change between deployments without changing the code; unlike configuration files, it is less likely to accidentally save them to the code repository; and unlike user configuration files or other configuration mechanisms, such as Java System Properties, they are a language and operating system independent standard.
Naturally, all this applies mainly to web applications. For desktop and mobile applications, these rules are no longer applicable.
From myself I will add that Azure-applications ASP.NET and ASP.NET WebAPI are now configured exactly this way: in the application panel on the Application settings tab, you can specify environment variables. Same in Heroku.
Example
In our project is required to send emails and SMS. Naturally, on the local contours of the developers and on the general contour of the development, you do not need to send anything, but you also need access to the content of the notifications. That is, developers should see that the notification service has worked, and see what exactly will be sent to the client on the combat circuit.
Since we use dependency injection , we have made several implementations of the classes that send notifications. The product implementation sends the letters, and the developers implementation writes notifications to the file. As an IoC library, we use Autofac, which allows us to register dependencies in a configuration file, so a notification service for developers was registered in our Web.config .
MSBuild can transform ASP.NET project configuration files during deployment. If your folder contains the Web.config and Web.Release.config , when you deploy the project in the Release configuration, MSBuild will apply the transformations from Web.Release.config to Web.config . You can change the attributes of sections, add and delete subsections. We changed the registered class, so that on the Release circuit we started the real notification service instead of the debugging one.
Then we are satisfied with this decision.
For a while, everything worked well, but then the 4th version of Autofac came out, which became compatible with the new .NET configuration system. At the same time, Autofac developers have cut support for the old method, that is, good old Web.config and App.config . However, MSBuild cannot automatically transform new configuration files.
I had to redo the scheme. Now for each contour we began to store our version of the IoC configuration in the files IoC.Dev.json , IoC.Stage.json , IoC.Release.json . Loading the desired configuration file was carried out as follows:
Startup.cs
var configName = Environment.GetEnvironmentVariable("APPSETTING_CONFIG_NAME") ?? "Dev"; var config = new ConfigurationBuilder().AddJsonFile($"IoC.{configName}.json", optional: true, reloadOnChange: true); var module = new ConfigurationModule(config.Build()); builder.RegisterModule(module);
It was a spontaneous solution that we found in Google and were able to apply to our Azure-project. However, this is not ideal in terms of a 12-factor application. Part of the configuration is indeed rendered into the environment variable, but part is in the IoC.*.json files.
What's wrong? The system administrator can decide that the files IoC.Dev.json , IoC.Stage.json , IoC.Release.json can be easily changed, although in fact we are rather severe about them. We do not need versatility and flexibility here, we would like to limit the setting to two options: a) send notifications; b) we put the notice in a secret place.
So we can hard-code these two strategies and at the start of the application choose the one that is specified in the environment variable:
var notifyStrategy = Environment.GetEnvironmentVariable("APPSETTING_NOTIFY_STRATEGY"); switch (notifyStrategy) { case "send": builder.RegisterType<ReadNotifier>().As<INotifier>(); break; case "save": builder.RegisterType<FakeNotifier>().As<INotifier>(); break; default: throw new ArgumentException("Ну всё теперь.", nameof(notifyStrategy)); }
Now the application administrator can configure it at his dive level. It will not break anything important in the wilds of XML / JSON IoC. As a result, we managed to completely get rid of the configuration files at this level and get closer to the ideals of 12-factor applications.
Based on this, I would advise:
All secret settings, including database connection strings, logins and passwords for sending emails, etc., are taken from the environment variables. Deploying an application is reduced to running a single command (for .NET it is MSBuild). Environment variables and the deployment process are described in the README.md .
Implement dependency injection directly in code, providing several strategies that the administrator will manage. He will thank you if he doesn’t have to study the details of the application, and he can, with one setting, implement a completely different set of three to five to ten types without understanding their interrelationships.
Some configuration files are in fact the declarative part of the code and do not need to be modified after deployment. Details depend on the language, I think that it is more often found in interpreted languages. We should not consider such files as true configuration files and can leave them in the project as it is.
The configuration that the administrator can change and which can be saved between deployments can be transferred to the database. It is possible and in the file, but in this case, it can be destroyed with inaccurate deployment. The values in the database or in the file, if they are not there when the system is first started, the standard ones from the code are written.