A few Google searches will turn up many different conversations about Metaprogramming…covering the good, the bad, and the beautifully elegant implementations.
(I recommend using “-ruby” to filter out all of the tutorials and books about Ruby’s implementation of Metaprogramming. For the record, this post is not about Ruby, at all.)
Metaprogramming, as I’m using the term, is a practice where a developer can write “generic” code and the details are then segregated out to a configuration file. Where the intended result is a more “readable” solution that follows the DRY Principle.
A secondary goal of Metaprogramming is to allow the system to be more “flexible” and configurable at runtime. Meaning, the developer doesn’t need to handle all possible options from the beginning. Or the developer doesn’t need to worry about building out a user-interface, because now the configuration file is the user-interface. Or the developer is able to focus on more important problems, like making beautifully elegant RegEx code that parses this free-form configuration file. (Bonus points if it is done as a one-liner.)
This style of Metaprogramming is extremely error prone. Here are three of the best (most painful) examples I have seen in my experience with Metaprogramming:
Missing configuration items…and the program runs without complaining
Yes, you read that correct. No, that is not a typo. I have seen a product that runs without any indication of a problem, even though a vital piece of information is missing from the configuration file…the device serial number. And the log files and error messages simply communicated that the device was not in the correct state. Upon further debugging, the device was not in the correct state, because the device didn’t even exist.
Invalid data causes non-obvious exceptions
I have experienced that some applications make assumptions about how you enter your configuration data. For example, I thought an empty string (“”) was perfectly acceptable in the configuration file, because I saw it used on a similar setting in the same file. I simply wanted the background color to be the default color. Instead, the application would run for a while, and then crash at seemingly random times. It turns out that it would throw a Null Reference Exception after a certain number of times the screen was refreshed. This one was a fun one to find.
Valid, yet incorrect, data yields varying results
I have witnessed a workflow getting run 8-10 times, to have 2 different exceptions thrown, 2-3 different error messages logged, and sometimes the application simply locked up with no helpful information. The answer…the configuration file was pointing at the wrong COM Port. This particular issue took 3 days to find the problem, and 30 seconds to fix…now that is what I call fun!
FACE PALM
Metaprogramming might allow you, as a developer, to move quickly. However, I guarantee that you will spend more time in the long run due to the following…
- writing validators for all of your configuration entries, and all possible formats of data entry
- properly handling missing or incorrect data
- properly designing your application to safely shut-down when the configuration data is wrong…and inform the user as to why the application shut-down
- maintaining proper scope of the configuration elements
- handling changes to the configuration at runtime
And I’m certain there are more pitfalls than listed here that you must focus on when using Metaprogramming. If your application needs this level of configuration and customization, then consider writing several smaller applications or using a plug-in system.
Do us all a favor…beware of Metaprogramming.