If I try to look at the path software made in its lifetime, I’d see that the amount of configuration required to build an application started with none, gradually grew bit by bit as more and more components were starardised until it reaches the state we’re in today, the too much configuration stage.
Quickly peeking at a J2EE application’s configuration file would reveal the horror and meaning of too much configuration. A huge, generally unmaintainable file, which essentially allows for configuration of anything in your application - Only that in practice, it allows almost nothing at all, a result of its size and complexity.
It comes with little wonder why Ruby on Rails‘ designers chose to attach it the slogan Write beautiful code favoring convention over configuration. They attack one of the major lackings in J2EE development: The lack of defaults. This added complexity and overhead was made into a target, and even though few argue that the deployment phase is an issue as well, it has its own benefits rendering it as a smaller issue to most.
It also comes with little wonder, and maybe with a sigh of relief, that with Java EE 5 its designers added the “convention” to the configuration. There are big amounts of defaults and a lot of the resources can be looked up according to their field’s name and type, making the redundant lookup code obsolete and a part of the past.
I gave RoR and J2EE as an example as it’s a very hot topic lately. However, many other frameworks are following this path for that kind of change, and some already have: Tapestry, Hibernate and JAXB, just to name a few. It’s obvious that using conventions and meta-data is the “next thing” in configuration, but as with anything new, it needs to be implemented wisely.
So, a few things to consider when writing a configurable framework:
- The rule of thumb: Any component you have should be made to be configurable, if possible. You can never know will be using your framework and what they’ll want to change in it - And changing a configuration file is always preferable to changing a framework’s code.
- Always, always make sure your conventions, defaults and meta-data are overridable by the configuration file. If they’re not, it’s the same as hard-coding your pieces together.
- Provide and use annotations. They are configuration tags brought into the language. Add meta-data to your classes, fields and methods using annotations so that they would correspond to your xml file and essentially replace the redundancy it sometimes represent. Provide annotations for the framework’s users so that they could annotate their classes, methods and fields instead of redundantly writing a configuration file.
- Use defaults. Even if a configurable entity was not configured, there should be some proper default to attach it with. If you do make use of defaults though, make sure to document it carefully to avoid confusions.
- Try to infer configuration from your users’ annotated elements, and not just from the annotations’ properties. The elements can, using reflection, provide a great detail about the annotated element such as the package it arrives from, the class its contained in, their name, etc. Reflection will give you a wide array of information to retrieve, and that information should certainly not be passed into the meta-data instance manually or written in a configuration file, forcing it to be maintained twice.
- Make sure to provide a proper validator to your configuration data with your framework. It could be a part of the framework or it could be an external application, but it should provide with precise information regarding which configuration is wrong or missing and how it can be fixed. When configurations are provided in multiple ways, it’s often confusing as to where a specific informaton is coming from, and proper validators make the life of a user much, much easier.