amw1 has asked for the wisdom of the Perl Monks concerning the following question:

I'm working on a project to break up our ball of mud. I'd like to introduce the notion of versioned APIs for packages (essentially different versions of a given package) using semantic versioning (semver.org). My goal is to allow for a structure like:
MyPackage use YourPackage version 1.0.3; use OtherPackage version 2.5.6; YourPackage use OtherPackage version 3.2.1

Essentially, I don't care what version of OtherPackage YourPackage uses, I only care that the version of YourPackage that I'm using is supported.

Our development team has gotten quite large (well over 200 folks), and trying to synchronize API changes across 5 different timezones and hundreds of developers is a pain. Having some way to allow devs to continue to use older versions of a package, while allowing package maintainers to move forward (i.e. break APIs) without having to worry about breaking everyone else would be fantastic. Obviously there would be a lifecycle, we would deprecate older versions of the packages as needed (and uplift the packages using the deprecated version etc).

Edit: Ideally I'd like this to be mostly on the packaging side. I'd like to avoid littering piles of conditionals in code to handle the versioning. i.e. none of ...

if(some_version){ do this; } elsif(some other version) { do this other thing; }

Replies are listed 'Best First'.
Re: Multiple package versions (use only modules cpanfile pinto carton local::lib install_base))
by Anonymous Monk on Jun 11, 2015 at 23:42 UTC
      Only doesn't seem to work anymore. (at least not on 10.10 which is the version we're still on.

      It's been a while since I've played with carton, but having to use carton exec to start the software is less than ideal imo, and I'm still not sure that it allows for the same application to be running multiple versions of a package. Picking a particular version of a library is pretty straight forward, it's running multiple at the same time that stumps me. Here's some code I put together to pick a particular version:

      package LoadModule; use strict; use warnings; my $wantedPackages; my $INC_LOADED; sub import { my $pkg = shift; my ($module, $version) = @_; $wantedPackages->{$module} = $version; push(@INC, \&loadLib) unless($INC_LOADED); $INC_LOADED = 1; } sub loadLib { my ($codeRef, $filename) = @_; my $moduleName = $filename; # Strip the implied pm $moduleName =~ s/\.pm$//; if(exists $wantedPackages->{$moduleName}) { my $package = $moduleName . "-" . $wantedPackages->{$moduleNam +e} . ".pm"; if(-e "./lib/$package") { my $fh; open($fh, "<", "./lib/$package") || die "Can't open ./lib/ +$package: $!"; return($fh); } } else { return undef; } } 1;
      Below is how loadModule is used
      package TestModule1; use LoadModule MyLib => 'v1'; use MyLib; sub doPrint { MyLib::foo(); } 1;

      In the example above I have

      lib/MyLib-v1.pm and lib/MyLib-v2.pm
      Both with slightly different behavior but with the same overall code.

      Tihs works great for the first module loaded. However, if I load other version of the same module they don't seem to get loaded (I suspect there's namespace collision).

        However, if I load other version of the same module they don't seem to get loaded (I suspect there's namespace collision).

        Yeah, namespaces are global

        If you're improving the API, why not improve the names?

        If you module was OOpy it could be much simpler, simply export a sub that returns the name of the subclass ... or just make sure the caller inherits from it

        You could simply disallow fully qualified names (MyLib::Foo()) and simply export the appropriate sub Foo

        And there is always source filters