I got to thinking about the Rediscovering Options post. The volatility analysis portion is an important artifact of my design process. The current version requires reading the whole list to comprehend the results. How could I make it better?

The design process is generally not very clean or binary. Many avenues are explored and abandoned. Even the important criteria change as understanding changes. However, the cleaned-up analysis in the post is tantalizingly reminiscent of a product pricing & feature table. A bit of spatial organization and some colored icons should allow quick intuitive interpretation of results and better recall.

Here’s my pedagogical periodical prototype.

For review, the design options are

  • Direct: Using a tool like ConfigurationManager to directly access configuration
  • Static: Using a global static utility for accessing configuration. We’ll assume named constant keys.
  • Accessor: Using a module-specific type for injecting configuration. Values are returned by methods
  • Options: Using a module-specific type for injecting configuration. Values are stored as properties.
DirectStaticAccessorOptions
Add a configuration valueEasy. Simply reference the key from desired componentEasy. Add a constant for the new value, and add a call in the relevant component.Easy-ish. Add a method to the relevant type. Potentially create a new config accessor and add it to the composition rootEasy. Add a property to the relevant type. Potentially create a new config data type and inject it to the composition root
Remove a configuration valueUnsafe. Configuration can be referenced from anywhere and so all code is susceptible to change. You must search and test the whole system to ensure the value is no longer used. Any remaining references will throw a runtime errorUnsafe, but easier. Configuration can be referenced from anywhere. You must check and test the every system that uses the configuration helper thoroughly because all code is susceptible to change. The search is aided by tooling. Remaining references will cause a compiler error because we're using named constants as value keys.Easy, safe. Any missed usage of deleted method will be caught at compile time. Since the accessor is created for a certain component, only that component needs to be retested.Easy, safe. Any missed usage of deleted method will be caught at compile time. Since the type is created for a certain component, only that component needs to be retested.
Change config storage (i.e. database, json, xml)Must change every module that uses configurationOnly need to change the static configuration helper, but every caller is potentially exposed to changed behavior or errors.The source can be changed per configuration value independently. Only the changed values need to be tested and redeployed.The source can be changed per configuration value independently. Only the changed values need to be tested and redeployed. It is likely that you will fetch all configuration values for a given type from the same sources though. Still only affects the one component
Pull from multiple config sources. (different values from different stores)Possible, have must change every component that wants a different storePossible but awkward. Requires internal lookup of config keys to expected storeEasily defined per valueEasily defined per type and not very hard per value
Pull from multiple config sources (same value coming from a prioritized list of sources)Explosive code duplicationPossible and centralized. Errors can leak to every caller and only show when the code path is executed.Possible, but not well centralized. Must change every accessor that needs multiple sources (or add another abstraction layer)Possible and centralized. Any errors happen at bind time. Most likely on application start with the creation of the composition root.
Run parallel tests with different configuration valuesDepends on framework. Probably not possible or at least non-trivialNot possiblePossible, requires mockingPossible, does not require mocking
Use same component with different configuration in different parts of the system (i.e. Connect to two databases to transfer data. Access a different data store with same schema for different use cases)Not possibleNot possiblePossible but not elegantPossible and easy
Enable configuration values per culture/languageDepends on framework, probably not possible. At least requires explosive code changesPossible, probably need to change every callerPossible and easy (can always inject the culture). At most need to change every config accessorPossible and easy. Probably only need to change composition root.
White label platform, load config per whitelabel customer allowing shared infrastructure with different behavior and potentionally different storage/resourcesSame as aboveSignificant concurrency issues.Same as aboveSame as above
Re-use component directly from a new client (i.e. console app as a quick utility)configuration needed by component is completely opaque. Have to look at code or run it and see errors to know what values are needed. The new client must reference the same configuration framework and have the same configuration resources (i.e. files)Same as direct, must also drag the static config utility into the new client. Helper likely exposes all of the possible configuration values from the rest of the system whether relevant or not.Either implement a new accessor or setup the same resources as the old use case.Trivial to manually bind configuration or use a different binding framework.
Move component to a new systemSame as aboveSame as aboveSame as aboveSame as above
Release as libraryCompletely untenableCompletely untenableAwkwardThe expected pattern