How not to recommend a Perl module

Reading time: 3 minutes

The first comments in response to my Anatomy of ylastic-costagent article were recommendations to consider other modules than the ones I used. Frankly, these module recommendations sucked, and I’m annoyed enough to explain why.

If you recommend a module, make sure it solves my problem 🔗︎

The first recommendation was to consider Date::Simple instead of Time::Piece + Time::Piece::Month, but Date::Simple doesn’t appear to solve my problem.

I was looking for a way to find the first day of the next month (without manually incrementing the month and potentially the year). Here’s how I did it:

# returns a Time::Piece object
Time::Piece::Month->new( Time::Piece->new() )->next_month->start()

Note that Time::Piece::Month has a next_month() method. Does Date::Simple offer a way to do that? Not that I can see in the documentation, so the recommendation is useless to me.

If you recommend a module, make sure I can see how to use it 🔗︎

The second recommendation was to use Log::Any::App instead of Log::Dispatchouli. Leaving aside that I said the reason I was using Log::Dispatchouli was because I was explicitly trying to learn it, the recommendation suggested that I could have “1 instead of 6” lines of code.

Here’s my “six” (actually about 8) lines:

local $ENV{DISPATCHOULI_PATH} = $opts->get_logpath
  if $opts->get_logpath;

my $logger = Log::Dispatchouli->new({
  ident     => basename($0),
  facility  => $opts->get_syslog ? $opts->get_syslog : undef,
  to_file   => $opts->get_logpath ? 1 : undef,
  log_pid   => 0,
  debug     => $opts->get_debug,
});

Note that I’m setting a name, optionally logging to syslogd, optionally logging to a file of a user-defined path (and potentially doing both), and toggling a debug mode.

Looking at Log::Any::App’s usage synopsis, here’s what I see:

use Log::Any::App qw($log);

Yes, that’s one line. But does it provide the same options? Obviously not in just that line. If I read to the end of the documentation, I find there is an init() function and I can do something like this (N.B. untested):

require Log::Any::App;
Log::Any::App::init([
  '$log',
  ($opts=>get_logpath ? (-file => $opts->get_logpath) : () ),
  ($opts=>get_syslog ? (-syslog => { facility => $opts->get_syslog) }) : () ),
  ($opts=>get_debug ? (-level => 'debug') : () ),
]);

I don’t know if that really does exactly the same thing – it looks like it would get close, but it’s certainly not “1 line versus 6”.

In evaluating the two modules, I compare whether documentation makes it easy to figure out how to use each module. Here are the two synopses together for comparison:

# Log::Any::App
use Log::Any::App qw($log);

# Log::Dispatchouli
my $logger = Log::Dispatchouli->new({
    ident     => 'stuff-purger',
    facility  => 'daemon',
    to_stdout => $opt->{print},
    debug     => $opt->{verbose}
})

One of those shows me how to configure it to suit my needs. The other doesn’t. Log::Dispatchouli also documents its new() method and all arguments within half a page after the synopsis. Log::Any::App buries its init() documentation towards the end of the documentation (and also doesn’t mention that it will take a variable name to export to, just like on a “use” line).

If the recommendation had said, “if you want a logger that gives you decent defaults without configuration”, I might have found the recommendation more useful, but that is not what it said. Unless it’s obvious from the docs, a recommendation should tell me how to do the same thing easier/faster/better than what I’ve already done. If it’s not obvious, write an example yourself, or don’t bother with the recommendation.

Log::Any::App actually seems decent, but if I wasn’t annoyed enough to write this article, I probably wouldn’t have bothered to read past its terrible synopsis.

•      â€¢      â€¢

If you enjoyed this or have feedback, please let me know by or