Wednesday, June 16, 2010

Configuration Design

How much thought goes into what your configuration file will look like when developing an application? That is, does the overall configuration structure have an impact on the quality of your finished product? This question can be asked from more than one perspective. For instance, from the end user's point of view, is the set of potential configuration options cohesive and easy to understand? The number of potential configuration options also plays a role in the overall manageability of the application because more configuration options means more complexity. This same point needs to be considered from the application's point of view. Adding flexibility by means of configurable options has an impact on your application's design. Making any aspect of your application configurable adds variability to something that could have been constant, or hard-coded. Beyond the size and structure of an application configuration, there is meaning behind each configuration option. Thinking about what each configuration option means before adding it to the configuration design is important. Lets take a look at an example of how we might go about designing a configuration schema for a simple system.

The system under development is a simple desktop GUI application data viewer. The application will support two data formats; JSON and XML. The GUI for this application is fairly straightforward in that it will have two tabs. These tabs will offer the end user a different view of the data. The first tab will use a tree widget to display a hierarchical view of the data. The second tab will use a table or grid widget to display the data. In the latter tab, clicking a row will reload the table or grid with that row's data if it can be displayed as a table or grid. Otherwise, a dialog is opened to display the details of the row data. Likewise, if a leaf node in the tree widget of the first tab is selected, the same dialog will appear. The application can load the data from a local file or from a URI on the web.

This intentionally simplistic system hardly justifies the need for having a configuration. It is ideal, however, for our purposes here because we want to elaborate on how the configuration values for the application come into being. Lets start by looking at the static aspects of our sample application. By static, I mean aspects of the application that remain constant throughout it's lifetime. These aspects aren't configurable. It is worth looking at the fundamental features that will not change before designing configurable elements. This way, we can eliminate candidates from our list of potential configuration values. Looking at the sample application under development, there are two supported data formats, JSON and XML. One potential configuration value we could add is a list of supported data formats. Logically, we would want to have the ability to extend our application to support more than just JSON and XML formats. However, in our case, this is something that will remain constant. The system is a JSON/XML reader so we don't want to change the what the application does best.

Now that we've eliminated a potential configuration option, lets think about some configuration categories, or sections. Configuration sections are a group of related configuration options. For instance, in our application, we would probably want a configuration section for GUI settings. The benefit to having configuration options grouped by sections is that the end user can get a better handle on what they're changing. Conceptually, sections, or categories, give the end user a little more confidence when manipulating the configuration. For instance, if a user sees a GUI section in the configuration file, they're going to feel at ease changing these options. This is because the intent of this particular group of options is clearly stated; change these values to alter the look and feel of the GUI. Additionally, from the developer's perspective, configuration sections give us an opportunity to map configuration options to concepts found in our code. GUI configuration options might map to only a handful of classes, or possibly a single base class.

Since our sample JSON/XML reader application is so simplistic, we're limited in the number of configuration sections we can create. Our application is a desktop GUI application so "gui" is a good candidate configuration section. We can also read JSON or XML from the web so "network" might be considered for a configuration section. Finally, we may also want a "core" section for configuration options that don't fit into either of the above sections. The "core" configuration section is intentionally general because we still need to leave room for changes. As our application code evolves and is re-factored, we may discover new concepts to map our configuration options to. The "gui" and "network" concepts, although broad, are clear enough for both users and developers upfront.

Lets take a step back from our sample application for a moment and take a look at some the various types of configuration options. The most basic type of configuration option we can have is a boolean, on/off value. These are the configuration options that either enable or disable some aspect of the system. With boolean configuration options, you typically specify a true value to enable something. For instance, setting "ssh_enabled" to a true value enables SSH. We can also use boolean configuration options to negate a certain aspect of the system. For instance, setting "ssh_disabled" to a true value disables SSH. In the latter case, we've changed the meaning the boolean configuration option to take on a negative meaning. Most of the time, the former approach of true values acting as an enabler works best because the intent is always clear. Configuration options can also specify a literal value such as a string or integer. These types of configuration values are useful for default values used in the system. Configuration options can also specify a list of literal values are also typically used as defaults within the system or as a means to extend the system.

Going back to our JSON/XML example, lets come up with some configuration values. Is there any aspect of the application we may like to disable at some point? Perhaps the obvious choice is to have the ability to disable the JSON format or to disable the XML format. What type of configuration option should we use to accomplish this? Two boolean configuration options, "json_enabled" and "xml_enabled", could be added to the configuration file. By default, our application would probably have these two options set to true since these options make up the core of the system. Next, what configuration section do these configuration options belong to? For now, they go in the "core" section because changing these options has an impact on several concepts in our application. If we were to set "xml_enabled" to false, the XML tab would no longer be displayed. An alternative to using boolean configuration options is a list. For instance, we could define a "data_formats" configuration option that defaults to a list of "JSON" and "XML". With this approach, there is a need for only a single configuration option instead of two. But, the problem with this approach is that we've introduced a notion of extensibility to both developers and the end user when this isn't the case. To disable one of these data formats, we'd have to remove it from the list. Once removed, it is as though the data format never existed.

What about our other two configuration sections, "network" and "gui"? In the network section we have a good opportunity to use a list configuration type. An "allowed_hosts" option would allow us to specify a list of hosts in which the end user is allowed to request data from. The "network" section could also use a "network_enabled" boolean option that allows us to disable networking entirely. Our "gui" configuration section is largely undefined at the moment. This is fine because the last thing we want to do is throw configuration options into sections when they aren't needed. The "gui" section is a good place to put proxy configuration options, if and when they are needed. Our application is going to use a GUI library of some sort and these libraries offer a plethora of parameters to change the look and feel of the application. The configuration options defined in the "gui" section can usually be passed directly through the application and into the GUI library.

What we have seen here with our simple example is that there is some thought involved with how a finished configuration file for an application should look. The options used in your configuration file should map well to the concepts found in your application design. If you're writing an application and something seems unclear about a configuration option, change it. But don't hesitate as the sooner you correct this ambiguity the better. Problems with configuration options may be badly named options or they may have misused values. These issues sometimes aren't trivial to spot but putting yourself in the mindset of someone who has to use your configuration file for productive work will often help with the design.