Skip navigation

Monthly Archives: July 2009

In the source repository, there are a number of changes to Perl::Critic that include deprecation of existing functions/methods. One Policy author has asked us to not do the deprecation because it will result in warnings being emitted whenever his Policy is being used.

There have been a lot of discussion recently in the Perl community about what deprecation means. For me, deprecation means that the interface or functionality will be removed and not sit around in a limbo state for years.

So how do I decide whether to deprecate something? If something is merely suboptimal, I’m not going to deprecate it; I’ll merely change the documentation to discourage its use. In Perl::Critic, there was the issue of Policy constructors. For a long time, there was no requirements for the new() method for a given Policy other than it returning a blessed hash. There are some Policys [sic] outside of the core distribution with new() methods that don’t call up to the new() method in Perl::Critic::Policy. There was a point when we really wanted to add a lot of functionality to the constructor, but we couldn’t because there were Policys that didn’t call it. We worked around this by doing what we wanted after the Policy object was constructed. If you look at the developer documentation for Perl::Critic, you’ll note that it discourages creation of a new() method.

On the other hand, if something is incorrectly designed or is holding up future changes, I’m going to change it or remove it. I’m not going to just work around the issue forever. I won’t change it without notifying anyone, however.

I made a mistake with the design of Perl::Critic::Utils::PPI::get_constant_name_element_from_declaring_statement(). I didn’t realize that you can actually create multiple values using the constant pragma; Tom Wyant corrected my misconception. The function returns a single value, so it can’t correctly handle single statements that declare multiple constants. Its interface is wrong. It will be removed in the future. A suggestion was made to immediately remove it, but we’ll go through a deprecation cycle first. We won’t change things without giving notice.

I think the Policy author’s question about the warnings comes from the practice of a number of projects of “deprecating” something, but never actually removing it. Sun is a particularly egregious example of this with Java functionality that has been deprecated for more than a decade but still hangs around, allowing people to write new code against an interface that is broken. If I deprecate something, it will be removed. The warnings that are emitted allow others to deal with the change before it happens. If, in the interim, this annoys some users, too bad. It would be worse if the code just stopped working.

If you have a look at my code page, among the epigrams that you find there is “If it’s broken, fix it.”. While there has long been the saying of not fixing something that isn’t broken, I think people don’t do the opposite enough. Don’t suffer breakage. Don’t put up with things that are wrong. Change things that need changing, even if it causes some short-term pain.

Under regular development conditions, I rarely use perlcritic. Most of my Perl::Critic usage is via Test::Perl::Critic for new code and Test::Perl::Critic::Progressive for existing code.

For code that I release to the CPAN, the Perl::Critic tests are in xt/author so that they don’t get run as part of normal tests that someone installing the modules runs. On someone else’s system, I don’t know what Perl::Critic version or add-on sets of policies they have, so those tests may fail there through no fault of my own.

For non-public code, the Perl::Critic tests are regular tests that get run with all the rest of the tests. I can do this because I control the specific version of Perl::Critic that I’m running and which add-on policies that I’ve got around.

Perl::Critic supports the concept of severity levels for policies and, by default, only applies the most severe ones (severity 5). I effectively ignore the severities by turning Perl::Critic’s strictness all the way to 1. Of course, there are policies that I don’t agree with or cannot use for practical reasons, so I disable those explicitly.

You may have noticed that above I used the word “tests” and not “test”. This is because I run Perl::Critic on my tests as well as my “real” code. The set of policies that I use on tests is different than I use on the rest of my code. Tests have fewer restrictions on them compared to regular code. So I have a test that applies to the contents of lib and bin using one perlcriticrc file and a seperate test that runs on the content of the t and xt directories with a different perlcriticrc. For tests, I disable the POD policies and Subroutines::ProtectPrivateSubs (tests are allowed to peek). Sometimes I also disable ValuesAndExpressions::ProhibitMagicNumbers, though I prefer not to.

(Perl::Critic actually uses three self-compliance tests: one for the regular code, one for the tests, and one that makes an additional pass over the Perl::Critic::Policy subclasses that places some additional requirements on them.)

The sets of add-on policies that I generally use are Perl::Critic::More, Perl::Critic::Bangs, Perl::Critic::Swift, and Perl::Critic::Moose. I’m thinking about starting to use Perl::Critic::Pulp, but I’d have to disable about half the policies in there.

There are a couple of cases in which I do use perlcritic.

First, if I’m surveying a codebase that is new to me, I will run perlcritic --statistics-only over it in order to get an idea of the state of affairs.

Second, presented with a set of code that has a large number of problems that need fixing, after running perlcritic --statistics-only on it, I’ll pick an individual policy that I want to fix violations of and run perlcritic -s policy name on the code. (The long form of -s is --single-policy.) I find it easier to fix one kind of problem across all of the files before moving on to another kind of problem than to fix all the problems in one file before moving on to another file.

So, I was reading chromatic’s latest, wherein he links to a p5p post which mentions the whole bit about @INC including multiple point releases in it, which makes having them around expensive. In case you didn’t know it, you can install perl point releases in the same directory and the default @INC will include all the releases in descending order, meaning that you can use binary modules from prior point releases without recompiling them.

But why?

If you’re going to go to the trouble of compiling a new perl executable, how much more effort is it to recompile everything else in your install? If you’re using continuous integration, don’t merely use it on your code, use it on the CPAN modules you use as well.

The whole idea of mixing point releases seems like it’s a nightmare waiting to happen.

Actually, why are you upgrading an existing perl installation at all? What if there’s some code that can’t deal even with a point release upgrade? You shouldn’t force that upgrade on others in your organization. Put the point release into a different directory and lock it down. Don’t use site_perl; install all of your modules into some private area and use PERL5LIB. (Learn to use INSTALL_BASE with ExtUtils::MakeMaker and install_base with Module::Build.)