I have been working with Perl 5 for the past few months and wanted to write about how to do Modern Perl Programming in 2017.


This is the second time I work professionally with Perl 5. While I do not consider myself an specialist on the subject I believe I have learned a few tricks, from practice and experienced programmers I met, that are worth sharing with a broad audience.

The post title comes from the fact that you can pretty much adopt most, if not all, modern programming practices while working on a legacy system, written in Perl.

Modern programming practices include, but are not limited to:

  • Clean Code
  • Test-Driven Development
  • Domain-Driven Design
  • Continuous Integration
  • Continuous Delivery
  • etc.

The Language

First of all, it is important understanding the programming language you are working with. Perl’s motto is: there is more than one way to do it. The Zen of Python is basically the opposite of it.

Given that, do not be too scared if you find the same operation being implemented in different ways on the same source code file. To be fair, the community have always been pushing for more consistency, so should you.

Perl allows you to write very brief code, which can be maintainable and readable, given the proper care. Perl is also a multi-paradigm language (supporting Object Oriented Programming, Functional Programming, etc.).

Modern languages such as Ruby, Python and PHP were influenced by Perl. Sometimes I got myself saying something such as “Oh! This is very similar to Ruby!”.

Practices and Tools

The product I am currently working at is a legacy system written in Perl 5 running on top of CentOS. The major goal of my project is adding new functionalities to this legacy system while keeping its existing behavior.

These are the practices and tools I adopted and helped me quite a lot:

1. Know and follow the language conventions

One of the most important things to do while learning and working with a language is knowing its conventions. Perl has its own, called perlstyle.

2. Adopt a tool for static source code analysis

Having a static source code analysis tool helps you writing Clean Code. You should consider integrating Perl::Critic to your Continuous Integration tool.

3. Always declare use strict and use warnings

Even if you are not new to the ecosystem, use strict and use warnings work as a safety net avoiding common mistakes and saving you a lot of time and headache.

4. If you are stuck with an error try use diagnostics

If you cannot figure out what is happening on your code, you can always add use diagnostics as a way to get a longer explanation. Just keep in mind that use diagnostics should only be used on your development environment.

5. Adopt the Test-Driven Design mindset

The lack of automated unit tests also made it harder for me to understand how the code worked and how my changes were impacting the existing behavior.

The strategy I chose was to build new functionalities into separated modules that could be built from day one following the Test-Driven Design approach.

During this process I learned a lot about Perl and found a few libraries that helped me delivering new features faster:

5.1. Test::Spec

If you are adding new features to a system you should consider adding automated tests, even if you are dealing with a legacy system. Test::Spec is a library that lets you write tests pretty much in the same way you do with RSpec.

Your tests will look like the following:

use Test::Spec;

describe "A feature" => sub {
  before each => sub {
    # your setup code goes in here

  it "should do something useful" => sub {
    # your test code goes in here

runtests unless caller;

5.2. Test::MockModule and Test::MockObject

While adding automated tests you might find useful mocking external modules.

6. Avoid the “one file to rule them all” anti-pattern

A common anti-pattern I have noticed while programming in Perl is the “one file to rule them all”, which means implementing everything on a single file. Independently of the programming paradigm you are using, you should always consider building smaller modules that could be composed into bigger ones.

7. Map the Domain Model using Object Oriented Programming

Moose is a framework that lets you write object oriented code while working in Perl.

package Message;

use Moose;

has 'content' => (
  is      => 'rw',
  isa     => 'Str',
  default => 'Hello, I am a message'
package Message::Welcome;

use Moose;

extends 'Message';

has 'content' => (default => 'Welcome!');

8. Use Functional Programming everywhere else

While working with Lists, for instance, you can use List::Util and List::MoreUtils.

Both libraries provide a nice functional interface for manipulating Lists.

use List::MoreUtils qw(all);

print "All values are non-negative"
  if all { $_ >= 0 } ($x, $y, $z);


Right now you might not feel compelled to learn Perl, but you would be surprised by how many businesses are still running on plataforms built decades ago.

The important point in here is understanding that even though these platforms are outdated, and not as cool as Go and Clojure, they still make millions in revenue.

Adopting modern programming practices might help both ends of this equation: your client, which is going to receive a better product, and you, which is going to benefit from a saner development workflow.