The perlcritic tool is often your first defense against awk­ward, hard to read, error-​prone, or uncon­ven­tion­al con­structs in your code,” per its descrip­tion. It’s part of a class of pro­grams his­tor­i­cal­ly known as lin­ters, so-​called because like a clothes dry­er machine’s lint trap, they detect small errors with big effects.” (Another such lin­ter is perltidy, which I’ve ref­er­enced in the past.)

You can use perlcritic at the com­mand line, inte­grat­ed with your edi­tor, as a git pre-​commit hook, or (my pref­er­ence) as part of your author tests. It’s dri­ven by poli­cies, indi­vid­ual mod­ules that check your code against a par­tic­u­lar rec­om­men­da­tion, many of them from Damian Conway’s Perl Best Practices (2005). Those poli­cies, in turn, are enabled by PPI, a library that trans­forms Perl code into doc­u­ments that can be pro­gram­mat­i­cal­ly exam­ined and manip­u­lat­ed much like the Document Object Model (DOM) is used to pro­gram­mat­i­cal­ly access web pages.

perlcritic enables the fol­low­ing poli­cies by default unless you cus­tomize its con­fig­u­ra­tion or install more. These are just the gen­tle” (sever­i­ty lev­el 5) poli­cies, so con­sid­er them the bare min­i­mum in detect­ing bad prac­tices. The full set of includ­ed poli­cies goes much deep­er, ratch­et­ing up the sever­i­ty to stern,” harsh,” cru­el,” and bru­tal.” They’re fur­ther orga­nized accord­ing to themes so that you might selec­tive­ly review your code against issues like secu­ri­ty, main­te­nance, com­plex­i­ty, and bug prevention.

My favorite above is prob­a­bly ProhibitEvilModules. Aside from the col­or­ful name, a devel­op­ment team can use it to steer peo­ple towards an organization’s favored solu­tions rather than dep­re­cat­ed, bug­gy, unsup­port­ed, or inse­cure” ones. By default, it pro­hibits Class::ISA, Pod::Plainer, Shell, and Switch, but you should curate and con­fig­ure a list with­in your team.

Speaking of work­ing with­in a team, although perlcritic is meant to be a vital tool to ensure good prac­tices, it’s no sub­sti­tute for man­u­al peer code review. Those reviews can lead to the cre­ation or adop­tion of new auto­mat­ed poli­cies to save time and set­tle argu­ments, but such work should be done col­lab­o­ra­tive­ly after achiev­ing some kind of con­sen­sus. This is true whether you’re a team of employ­ees work­ing on pro­pri­etary soft­ware or a group of vol­un­teers devel­op­ing open source.

Of course, rea­son­able peo­ple can and do dis­agree over any of the includ­ed poli­cies, but as a rea­son­able per­son, you should have good rea­sons to dis­agree before you either con­fig­ure perlcritic appro­pri­ate­ly or selec­tive­ly and know­ing­ly bend the rules where required. Other CPAN authors have even pro­vid­ed their own addi­tions to perlcritic, so it’s worth search­ing CPAN under Perl::Critic::Policy::” for more exam­ples. In par­tic­u­lar, these community-​inspired poli­cies group a num­ber of rec­om­men­da­tions from Perl devel­op­ers on Internet Relay Chat (IRC).

Personally, although I adhere to my employer’s stan­dard­ized con­fig­u­ra­tion when test­ing and review­ing code, I like to run perlcritic on the bru­tal” set­ting before com­mit­ting my own. What do you pre­fer? Let me know in the com­ments below.


Cover image: Everyone’s a crit­ic — graifit­ti under Mancunian Way in Manchester” by Alex Pepperhill is licensed under CC BY-​ND 2.0

black and gray audio mixer

Pretty soon after I start­ed writ­ing Perl in 1994, I noticed that it lacked a con­struct often found in oth­er lan­guages: the switch state­ment. Not to wor­ry, though — you can achieve the same effect with a cas­cad­ing series of if-elsif state­ments, right?

Well, no, you should­n’t do that, espe­cial­ly if the chain is real­ly long. There’s even a perl­crit­ic pol­i­cy about it, which sug­gests that you use given and when instead.

But given and when (and the smart­match oper­a­tor they imply, ~~) are still con­sid­ered exper­i­men­tal, with behav­ior sub­ject to change. So what’s a respon­si­ble devel­op­er to do?

The answer is to use the for state­ment as a top­i­cal­iz­er, which is a fan­cy way of say­ing it assigns its expres­sion to $_. You can then use things that act on $_ to your heart’s con­tent, like reg­u­lar expres­sions. Here’s an example:

for ($foo) {
    /^abc/ and do {
        ...
        last;
    };
    /^def/ and do {
        ...
        last;
    };
    # FALL THRU
    ...
}

This will cov­er a lot of cas­es (haha, see what I did there? A lot of lan­guages use a case state­ment… oh, nev­er mind.) And if all you’re doing is exact string match­ing, there’s no need to bring in reg­ex­ps. You can use a hash as a lookup table:

my %lookup = (
    foo => sub { ... },
    bar => sub { ... },
);
$lookup{$match}->();

EDIT: If every alter­na­tive is assign­ing to the same vari­able, a ternary table is anoth­er pos­si­bil­i­ty. This is a chained set of ternary con­di­tion­al (? :) oper­a­tors arranged for read­abil­i­ty. I first heard about this tech­nique from Damian Conway’s Perl Best Practices (2005).

           # Name format                 # Salutation
my $salute = $name eq ''                 ? 'Dear Customer'
           : $name =~ /(Mrs?[.]?\s+\S+)/ ? "Dear $1"
           : $name =~ /(.*),\s+Ph[.]?D/  ? "Dear Dr. $1"
           :                               "Dear $name"
           ;

Note that although this is just as inef­fi­cient as a cascaded-if/​elsif, it’s more clear that it’s a sin­gle assign­ment. It’s also more com­pact, and reads like a table with columns of match­es and alternatives.

Any of these pat­terns are prefer­able to cas­cad­ing if/​elsifs. And if you want to mon­i­tor the devel­op­ment of given, when, and ~~, check this issue on GitHub. It was last com­ment­ed on eight years ago, though, so I would­n’t hold my breath.