Breaking users of old versions of a module

Suppose that for some reason you really, really need to introduce a silent, backward-incompatible change for a module. For example, in 0.03 and earlier foo() accepts ($a, $b) as arguments but you need to change it to ($b, $a). After releasing v0.04, you probably also want to break code that says any of the following:

use MyModule; # no version specified
use MyModule 0.01;
use MyModule 0.02;
use MyModule 0.03;

by, e.g., croaking with a message like "Order of arguments of foo() changed in 0.04, please use older version or update your code. Program aborted."

Reading up on "perldoc -f use" and browsing on CPAN, I found version::Limit which does almost that. You can put something like this in

use version::Limit;
    "[0.0,0.4)" => "Order of arguments of foo() changed in 0.04",

version::Limit works by installing a VERSION() method into your module's package. This method is called by Perl when user is use-ing some module with a version requirement. But unfortunately, if user does not specify explicit version requirement, VERSION() will not be called.

If the change is backward-incompatible enough, you probably want to break use MyModule; (loading a module without version requirement) too.

One of the ways to do this is to also install the version checking routine into import(). But perhaps there are better ways?


One technique would be something like:

my %want;
   my $class = shift;
   $want{+caller} //= int($_[0]) if @_;
sub foo {
   my ($a, $b) = @_;
   ($b, $a) = ($a, $b) if $want{+caller} >= 2;

That way, if your caller imports your module as:

use Your::Module 1.01;

... they get the 1.x behaviour, but if they import it as:

use Your::Module 2.07;

... then they get the 2.x behaviour.

If your module is an exporter, better would be to use something like a Sub::Exporter-style generated sub; so the caller can elect which version of foo they wish to import. And if your module is OO, better would be to create a new method with a different name, and make the old foo an alias with different argument order:

sub foo {
   my $self = shift;
   $self->new_method(reverse @_);

Within foo you could also do something like:

$want{+caller} //= do {
   carp("You did not specify a version; assuming latest");
   int(our $VERSION);

VERSION would be called before import, so you can flag whether it was or not:

package Foo;

our $VERSION = 1;

my $checked_version;

my $self = shift;
$checked_version = 1;
$self->SUPER::VERSION(@_); # or do your own minimum version check here

sub import {
die "version not checked" unless $checked_version;


[Wow, the preview always has the code formatted strangely no matter what combination of tags I try. Oh, well. You can see the point.]

You could also have multiple implementation modules behind the scenes, and pick which one to back onto based on the version specified when use'ing the module.

xdg: I think the trick to getting good formatting for code in the comments here is to make sure there are not any blank lines between <pre> and </pre>. If you need a blank line, just make sure it's got a couple of spaces on it.

Leave a comment

About Steven Haryanto

user-pic A programmer (mostly Perl 5 nowadays). My CPAN ID: SHARYANTO. I'm sedusedan on perlmonks. My twitter is stevenharyanto (but I don't tweet much). Follow me on github: sharyanto.