Gradual method renaming in Perl
Tags: oop, perl, refactoring
Published: 2021-06-29 14:20:55

---
title: Gradual method renaming in Perl
published: true
date: 2021-06-29 14:00:00 UTC
tags: OOP,Perl,refactoring
canonical_url: https://phoenixtrap.com/2021/06/29/gradual-method-renaming-in-perl/?utm_source=rss&utm_medium=rss&utm_campaign=gradual-method-renaming-in-perl
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/64wj4se9isz5qfn3ynfp.jpeg
---

We have a huge codebase of over 700,000 lines of Perl spread across a couple dozen [Git](https://www.git-scm.com/) repositories at [work](https://www.hostgator.com/). Sometimes [refactoring](https://www.refactoring.com/) is easy if the classes and methods involved are confined to one of those repos, but last week we wanted to rename a method that was potentially used across many of them without having to QA and launch so many changes. After getting some help from [Dan Book](https://grinnz.net/) and [Ryan Voots](https://www.simcop2387.info/) on the [#perl libera.chat IRC channel](https://web.libera.chat/#perl), I arrived at the following solution.

First, if all you want to do is alias the new method call to the old while making the least amount of changes, you can just do this:

```perl
*new_method = \&old_method;
```

This takes advantage of Perl’s [typeglobs](https://perldoc.pl/perldata#Typeglobs-and-Filehandles) by assigning to the new method’s name in the symbol table a [reference](https://perldoc.pl/perlref) (indicated by the `\` character) to the old method. [Methods are just subroutines](https://perldoc.pl/perlobj#A-Method-is-Simply-a-Subroutine) in Perl, and although you don’t need the `&` character when calling one, you do need it if you’re passing a subroutine as an argument or creating a reference, as we’re doing above.

I wanted to do a bit more, though. First, I wanted to log the calls to the old method name so that I could track just how widely it’s used and have a head start on renaming it elsewhere in our codebase. Also, I didn’t want to fill our logs with those calls—we have enough noise in there already. And lastly, I wanted future calls to go directly to the new method name without adding another stack frame when using `caller` or [Carp](https://perldoc.pl/Carp).

With all that in mind, here’s the result:

```perl
sub old_method {
    warn 'old_method is deprecated';
    no warnings 'redefine';
    *old_method = \&new_method;
    goto &new_method;
}

sub new_method {
    # code from old_method goes here
}
```

Old (and not-so-old) hands at programming are probably leaping out of their seats right now yelling, “YOU’RE USING GOTO! GOTO IS [CONSIDERED HARMFUL](https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf)!” And they’re right, but this isn’t Dijkstra’s goto. From the Perl manual:

> The `goto &NAME` form is quite different from the other forms of [`goto`](https://perldoc.pl/functions/goto). In fact, it isn’t a goto in the normal sense at all, and doesn’t have the stigma associated with other gotos. Instead, it exits the current subroutine (losing any changes set by [`local`](https://perldoc.pl/functions/local)) and immediately calls in its place the named subroutine using the current value of [`@_`](https://perldoc.pl/perlvar#%40_). _[…]_ After the [`goto`](https://perldoc.pl/functions/goto), not even [`caller`](https://perldoc.pl/functions/caller) will be able to tell that this routine was called first.
> 
> <cite>[perlfunc](https://perldoc.pl/perlfunc) manual page</cite>

Computer scientists call this _tail call elimination_. The bottom line is that this achieves our third goal above: immediately jumping to the new method as if it were originally called.

The other tricky bit is in the line before, when we’re redefining `old_method` to point to `new_method` while we’re still inside `old_method`. (Yes, you can do this.) If you’re running under `use warnings` (and we are, and [you should](https://perlmaven.com/always-use-warnings)), you first need to disable [that warning](https://perldoc.pl/perldiag#Subroutine-%25s-redefined). Later calls to `old_method` will go straight to `new_method` without logging anything.

And that’s it. The next step after launching this change is to add a story to our [backlog](https://www.scaledagileframework.com/team-backlog/) to monitor our logs for calls to the old method, and gradually refactor our other repositories. Then we can finally remove the old method wrapper.