Here is the guidance I usually provide:

Problem Statement:

There are application features and behaviors that all users need, majority of them need, a minority need, some need. Users have personal preferences and individual information that the software needs to know.

We tend to handle some of the above using code, some using configurations.

Guidance #1

Configuration is not a solution across the board. Configurations are often a premature optimization towards saving future efforts.

Code _is_ configuration for the processor. Coding has development methodologies and tools designed by the industry over decades, most of which is not applicable to configurations. So don't make code run-time configurable. Change the code as and when needed. Refactor.

Exceptions:

- For personal preferences and individual information.

- If runtime behavior of the code must be changeable without rebuilding the code.

Guidance #2

If the software behavior can be changed with lesser number of lines of configuration than the number of lines of code, develop better abstractions in the code. (Do not invent DSLs, create better abstractions in the code itself.)

Keep code configurable via hard coded configurations at an appropriate place somewhere within the code. This encourages modularity. However, limit the flexibility to at most 30% development effort overall overheads above and beyond the currently known requirements. If development efforts overheads for the flexibility is much more, that flexibility is a premature optimization (keeping in mind, you aready have flexibility via ability to change the code). If you are not thinking above and beyond the currently best known requirements, you may find the requirements changing faster than what you can keep pace with.

Guidance #3

Instead of configurations, find a more specific alternative.

- Machine Learning models are technically code configurations, though we do not see it that way. ML comes with needed tooling to manage.

- Knowledge graphs.

- Data exchange file formats.

- Etc.

Guidance #4

Configurations are not for SDEs.
Identify the owner who would be responsible for changing the configurations and see them as customers, in the current phase of development. Think of what help and tools are you providing them to manage the configurations.

Guidance #5

Do not let the space of configurations multiply. Configurations parameters must be modular (i.e., independent) just like code.

Just as functions having more than three parameters should be avoided, same applies to configurations impacting the behavior of a function. Avoid more than three of them taken together for any function.

Guidance #6

A configuration is also a contract. Pay no less attention to it than to function interface or API design.

Configurations need to be equivalently documented. Think of them as command line arguments. If the user needs to know the implementation internals to understand the command-line arguments, default against having them.

Guidance #7

Backward compatibility and blast radius reduction are not valid arguments to have distributed configurations. Depend on automated and manual testing instead. Reason correctly about how many of the users would need the variation in software behavior.

If a code change is to be made (i.e., it makes sense), try to apply it everywhere. (I presume when Microsoft went to fixed and mandatory Windows update cycles, it would have helped them a lot.)

Guidance #8

If different Product Managers serving different regions or types of users ask for different requirements and you as developers see no valid reason for it, make them talk to each other and document their collective reasoning before getting back to you.