App Config Design

Keeping your configs clean and DRY

I wrote about DRY configs many years ago but astonishingly my clients don’t seem to read my blog. I mean, who even has a blog these days anyway?

Externalized application configs have a way of multiplying like rabbits to the point where people become afraid to touch them at all, let alone apply the DRY principle to them. The proliferation of poorly-designed configuration files in Enterprise Java systems is appalling, and the situation is compounded when an external configuration system like Consul or Spring Cloud Config is added to the mix.

Spring Boot went to great lengths to standardize how configuration files are loaded in a Spring Boot application so we owe it to our future selves to understand it, and put it into practice.

The most common mistake I see is systems that misuse environment-specific configuration with Spring Profiles to duplicate an environment-agnostic property. Here’s an example:

src/main/resources/application.properties:

some.key = Some Value

src/main/resources/application-test.properties:

some.key = Some Value

src/main/resources/application-prod.properties:

some.key = Some Value

And then in the config Server, we’ll have the same values duplicated

/myapp.properties

some.key = Some Value

/myapp-test.properties

some.key = Some Value

/myapp-prod.properties

some.key = Some Value

This property is clearly the same everywhere and should only be set once. The developer has copy-pasted the config file into 2(n + 1) separate locations, where n = number of environments.

This duplication completely invalidates the testing process for this application because the value that was tested in the test env could be completely different from the value that will be used in production.

The example is simplistic and easy to correct but this post is inspired by an Enterprise Java codebase that I recently encountered where all of the SQL used by the application was defined in this way. Each SQL query was hundreds of lines long with random whitespace making it nearly impossible to determine if it was actually the same everywhere. Correcting this has required many days of diligent work comparing hundreds of configuration keys across no less than 8 different environments and 3 git repos (one for the app code, and two for Spring Cloud Config files because prod is separate, and restricted. FML.) It’s a high-risk refactoring - one small mistake could break production.

Here’s how I recommend you structure your Spring Boot config files

  1. src/main/resources/application.properties should contain all of the common configs used in your application.
  2. src/main/resources/application-${env}.properties is only for configs thare are specific to the ${env} environment, such as database URLs, feature flag defaults etc
  3. configserver:app-name.properties generally should NOT exist.
  4. configserver:app-name-${env}.properties should only contain values that were changed after the application was deployed, such as feature flags that have been activated.

It doesn’t matter if you use properties or YAML. But please, don’t duplicate the common values! Use -local for your laptop/workstation environment.

Your application should start in a reasonable state even if your config server is unavailable. This is of critical importance, and is why I always try to minimize the number of settings in our config server.

If we change a value in configserver:app-name-${env}.properties after a deployment, then in the next release we move it from configserver:app-name-${env}.properties to src/main/resources/application-${env}.properties to make sure the default is correct if the config server is unavailable.