Modern Perl 5.36+ idioms, best practices, and conventions for building robust, maintainable Perl applications. USE WHEN writing new Perl code or modules, reviewing Perl for idiom compliance, refactoring legacy Perl, or migrating pre-5.36 code to modern standards.
Modern Perl 5.36+ idioms, best practices, and conventions for building robust, maintainable Perl applications. USE WHEN writing new Perl code or modules, reviewing Perl for idiom compliance, refactoring legacy Perl, or migrating pre-5.36 code to modern standards.
origin
ECC
cluster
systems-languages
version
1.0.0
Modern Perl Development Patterns
Idiomatic Perl 5.36+ patterns and best practices for building robust, maintainable applications.
When to Activate
Writing new Perl code or modules
Reviewing Perl code for idiom compliance
Refactoring legacy Perl to modern standards
Designing Perl module architecture
Migrating pre-5.36 code to modern Perl
How It Works
Apply these patterns as a bias toward modern Perl 5.36+ defaults: signatures, explicit modules, focused error handling, and testable boundaries. The examples below are meant to be copied as starting points, then tightened for the actual app, dependency stack, and deployment model in front of you.
Core Principles
1. Use v5.36 Pragma
A single use v5.36 replaces the old boilerplate and enables strict, warnings, and subroutine signatures.
# Good: Modern preambleusev5.36;
subgreet($name) {
say"Hello, $name!";
}
# Bad: Legacy boilerplateuse strict;
use warnings;
use feature 'say', 'signatures';
no warnings 'experimental::signatures';
subgreet{
my ($name) = @_;
say"Hello, $name!";
}
2. Subroutine Signatures
Use signatures for clarity and automatic arity checking.
usev5.36;
# Hash and array referencesmy$config = {
database => {
host =>'localhost',
port =>5432,
options => ['utf8', 'sslmode=require'],
},
};
# Safe deep access (returns undef if any level missing)my$port = $config->{database}{port}; # 5432my$missing = $config->{cache}{host}; # undef, no error# Hash slicesmy%subset;
@subset{qw(host port)} = @{$config->{database}}{qw(host port)};
# Array slicesmy@first_two = $config->{database}{options}->@[0, 1];
# Multi-variable for loop (experimental in 5.36, stable in 5.40)use feature 'for_list';
no warnings 'experimental::for_list';
formy ($key, $val) (%$config) {
say"$key => $val";
}
File I/O
Three-Argument Open
usev5.36;
# Good: Three-arg open with autodie (core module, eliminates 'or die')use autodie;
subread_file($path) {
openmy$fh, '<:encoding(UTF-8)', $path;
local$/;
my$content = <$fh>;
close$fh;
return$content;
}
# Bad: Two-arg open (shell injection risk, see perl-security)open FH, $path; # NEVER do thisopen FH, "< $path"; # Still bad — user data in mode string
cpanm App::cpanminus Carton # Install tools
carton install # Install deps from cpanfile
carton exec -- perl bin/myapp # Run with local deps
# cpanfile
requires 'Moo', '>= 2.005';
requires 'Path::Tiny';
requires 'JSON::MaybeXS';
requires 'Try::Tiny';
on test =>sub{
requires 'Test2::V0';
requires 'Test::MockModule';
};
Quick Reference: Modern Perl Idioms
Legacy Pattern
Modern Replacement
use strict; use warnings;
use v5.36;
my ($x, $y) = @_;
sub foo($x, $y) { ... }
@{ $ref }
$ref->@*
%{ $ref }
$ref->%*
open FH, "< $file"
open my $fh, '<:encoding(UTF-8)', $file
blessed hashref
Moo class with types
$1, $2, $3
$+{name} (named captures)
eval { }; if ($@)
Try::Tiny or native try/catch (5.40+)
BEGIN { require Exporter; }
use Exporter 'import';
Manual file ops
Path::Tiny
blessed($o) && $o->isa('X')
$o isa 'X' (5.32+)
builtin::true / false
use builtin 'true', 'false'; (5.36+, experimental)
Anti-Patterns
# 1. Two-arg open (security risk)open FH, $filename; # NEVER# 2. Indirect object syntax (ambiguous parsing)my$obj = new Foo(bar =>1); # Badmy$obj = Foo->new(bar =>1); # Good# 3. Excessive reliance on $_map { process($_) } grep { validate($_) } @items; # Hard to followmy@valid = grep { validate($_) } @items; # Better: break it upmy@results = map { process($_) } @valid;
# 4. Disabling strict refsno strict 'refs'; # Almost always wrong
${"My::Package::$var"} = $value; # Use a hash instead# 5. Global variables as configurationour$TIMEOUT = 30; # Bad: mutable globaluse constant TIMEOUT =>30; # Better: constant# Best: Moo attribute with default# 6. String eval for module loadingeval"require $module"; # Bad: code injection riskeval"use $module"; # Baduse Module::Runtime 'require_module'; # Good: safe module loading
require_module($module);
Remember: Modern Perl is clean, readable, and safe. Let use v5.36 handle the boilerplate, use Moo for objects, and prefer CPAN's battle-tested modules over hand-rolled solutions.