Keep your programmatic configuration DRY

A common mantra among programmers today is to keep your code DRY. This little acronym stands for “Don’t Repeat Yourself” and serves as a reminder that when you see a repetitive pattern in your code or are tempted to copy/paste some statements, you should think twice and consider extracting the common logic into a chunk of code that can be reused.

For many programmers, this practice begins to break down when “configuration” code is involved. When I talk about configuration code here, I’m not talking about the XML, YAML, INI, etc. bits of your project. I’m talking about the Perl code in your program that simply serves as data to feed some active portion of your code.

A common mantra among programmers today is to keep your code DRY. This little acronym stands for “Don’t Repeat Yourself” and serves as a reminder that when you see a repetitive pattern in your code or are tempted to copy/paste some statements, you should think twice and consider extracting the common logic into a chunk of code that can be reused.

For many programmers, this practice begins to break down when “configuration” code is involved. When I talk about configuration code here, I’m not talking about the XML, YAML, INI, and other non-moving bits of your project. I’m talking about the Perl code in your program that simply serves as data to feed some active portion of your code. For example:

my @anchors = (
  {
    text => 'Effective Perl',
    href => 'http://www.effectiveperlprogramming.com'
  },
  {
    text => 'Perl',
    href => 'http://www.perl.org'
  },
);

for my $anchor (@anchors) {
    create_anchor_tag($anchor);
}

Here I have avoided repeating myself by consolidating all of the common logic for anchor processing into create_anchor_tag. My code loops the configuration data structure and simply feeds a little bit of configuration at a time into that subroutine.

This type of configuration works most of the time, especially when code is first written. Unfortunately, it breaks down quickly as the configuration becomes more complex over time. Say for instance that we now have more anchors and need to be more specific about the details of each anchor.

my @anchors = (
  {
    text  => 'Effective Perl',
    href  => 'http://www.effectiveperlprogramming.com',
    class => 'standard',
    style => '',
    lang  => 'en',
    dir   => 'ltr'
  },
  {
    text  => 'Perl',
    href  => 'http://www.perl.org',
    class => 'standard',
    style => '',
    lang  => 'en',
    dir   => 'ltr'
  },
  {
    text  => 'Perl Foundation',
    href  => 'http://www.perlfoundation.org',
    class => 'standard',
    style => '',
    lang  => 'en',
    dir   => 'ltr'
  },
  {
    text  => 'Perl6 ??????',
    href  => 'http://www.perl6.ru',
    class => 'standard',
    style => '',
    lang  => 'ru',
    dir   => 'ltr'
  },
  # ... and so on
);

You can see, that beyond simply repeating the hash keys over and over, I am also repeating quite a few of the hash values. For instance, the values for class, lang, and dir are fairly consistant. When you see this pattern occurring in your code, it is time to do a little refactoring. Luckily, refactoring repetitiveness out of code is often very easy.

A common strategy for refactoring is to simply inline a map statement into the configuration setup. In the example below, I have inserted a map that handes the portions of the configuration that change the least. It provides default values that represent the most commonly used configuration values and also allows for overrides by overlaying the anchor-specific hash at the end of the map.

my @anchors = map { {
    class => 'standard',
    style => '',
    lang  => 'en',
    dir   => 'ltr',
    %{$_}
  } } (
  {
    text => 'Effective Perl',
    href => 'http://www.effectiveperlprogramming.com'
  },
  {
    text => 'Perl',
    href => 'http://www.perl.org'
  },
  {
    text => 'Perl Foundation',
    href => 'http://www.perlfoundation.org'
  },
  {
    text => 'Perl6 ??????',
    href => 'http://www.perl6.ru',
    lang => 'ru'
  },
  # ... and so on
);

This simple tweak to the configuration setup significantly reduces the amount of redundancy in the code. Programmers who maintain this code can quickly see what the default values are for all of our anchor tags and can also more easily see the tags with customizations. When all of the options were specified for every anchor, it was easy to miss the meaningful configuration that made the anchors unique. There was way too much noise with the repeated code.

We can take our DRY’ness a step further now by removing the repeated hash keys in the configuration. Every anchor will have a different bit of text and a different link, so those keys will be present for every single configuration. Why declare them every time? Well, one reason is that it serves as good documentation. However, when you only have a couple of required options, positional notation is fine.

In this example, I’m storing each anchor’s configuration in an anonymous array. I’m assuming that the first two elements of the array are always the text and link for the anchor. Everything else is a customization.

my @anchors = map { {
    text  => shift @{$_},
    href  => shift @{$_},
    class => 'standard',
    style => '',
    lang  => 'en',
    dir  => 'ltr',
    @{$_}
  } } (
    [ 'Effective Perl' => 'http://www.effectiveperlprogramming.com' ],
    [ 'Perl' => 'http://www.perl.org' ],
    [ 'Perl Foundation' => 'http://www.perlfoundation.org' ],
    [ 'Perl6 ??????' => 'http://www.perl6.ru', lang => 'ru' ],
    # ... and so on
);

Now the configuration is very succinct and easy to read compared to its wordy successor. There is very little noise and no repeated code. Each configured anchor is as customizable as when we started. The common elements are only mentioned once and the differences are all that we see.