Communitygithub.com

perl-patterns

Patrones modernos de Perl 5.36+, mejores prácticas y convenciones para construir aplicaciones Perl robustas y mantenibles.

¿Qué es perl-patterns?

Esta habilidad proporciona un repositorio completo de patrones y mejores prácticas para escribir código Perl moderno (5.36+). Es esencial para desarrolladores que inician nuevos proyectos en Perl, revisan código heredado o migran aplicaciones antiguas a estándares contemporáneos. La guía cubre seis áreas fundamentales: pragmas modernos como `use v5.36`, firmas de subrutinas, manejo de contexto, desreferenciación postfija, el operador `isa`, y estructuras de control de errores como `eval/die`, `Try::Tiny` y `try/catch` nativo (5.40+). También aborda la programación orientada a objetos moderna con `Moo`, roles, y la palabra clave `class` experimental (5.38+). Incluye secciones sobre expresiones regulares con capturas nombradas y patrones precompilados, manejo seguro de archivos (open de tres argumentos, `Path::Tiny`), y organización de módulos con importadores y diseño estándar de proyecto. Complementa con guías de herramientas como `perltidy` y `perlcritic`, gestión de dependencias con `cpanfile` y `Carton`, y una tabla de referencia rápida que mapea patrones heredados a sus reemplazos modernos. Finalmente, enumera antipatrones críticos a evitar, como el open de dos argumentos o el uso excesivo de variables globales, promoviendo un código limpio, legible y seguro.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/perl-patterns

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Patrones Modernos de Desarrollo en Perl

Patrones idiomáticos de Perl 5.36+ y mejores prácticas para construir aplicaciones robustas y mantenibles.

Cuándo Activarlo

  • Al escribir código o módulos nuevos en Perl
  • Al revisar código Perl para verificar el cumplimiento de normas idiomáticas
  • Al refactorizar código Perl heredado hacia estándares modernos
  • Al diseñar la arquitectura de un módulo Perl
  • Al migrar código anterior a 5.36 hacia Perl moderno

Cómo Funciona

Aplica estos patrones como una inclinación hacia los valores predeterminados modernos de Perl 5.36+: firmas, módulos explícitos, manejo enfocado de errores y límites testeables. Los ejemplos siguientes están pensados para copiarse como puntos de partida y luego ajustarse según la aplicación real, la pila de dependencias y el modelo de despliegue que tengas delante.

Principios Fundamentales

1. Usar el Pragma v5.36

Un único use v5.36 reemplaza el antiguo código repetitivo y habilita strict, warnings y firmas de subrutinas.

# Bien: Preámbulo moderno
use v5.36;

sub saludar($nombre) {
    say "¡Hola, $nombre!";
}

# Mal: Código repetitivo heredado
use strict;
use warnings;
use feature 'say', 'signatures';
no warnings 'experimental::signatures';

sub saludar {
    my ($nombre) = @_;
    say "¡Hola, $nombre!";
}

2. Firmas de Subrutinas

Usa firmas para mayor claridad y verificación automática de aridad.

use v5.36;

# Bien: Firmas con valores por defecto
sub conectar_bd($host, $puerto = 5432, $tiempo_espera = 30) {
    # $host es obligatorio, los otros tienen valores por defecto
    return DBI->connect("dbi:Pg:host=$host;port=$puerto", undef, undef, {
        RaiseError => 1,
        PrintError => 0,
    });
}

# Bien: Parámetro slurpy para argumentos variables
sub mensaje_log($nivel, @detalles) {
    say "[$nivel] " . join(' ', @detalles);
}

# Mal: Desempaquetado manual de argumentos
sub conectar_bd {
    my ($host, $puerto, $tiempo_espera) = @_;
    $puerto       //= 5432;
    $tiempo_espera //= 30;
    # ...
}

3. Sensibilidad al Contexto

Comprende el contexto escalar frente al de lista, un concepto fundamental de Perl.

use v5.36;

my @items = (1, 2, 3, 4, 5);

my @copia  = @items;            # Contexto de lista: todos los elementos
my $cuenta = @items;            # Contexto escalar: cantidad (5)
say "Items: " . scalar @items; # Forzar contexto escalar

4. Desreferenciación Postfija

Usa la sintaxis de desreferenciación postfija para mejorar la legibilidad con estructuras anidadas.

use v5.36;

my $datos = {
    usuarios => [
        { nombre => 'Alicia', roles => ['admin', 'usuario'] },
        { nombre => 'Roberto', roles => ['usuario'] },
    ],
};

# Bien: Desreferenciación postfija
my @usuarios = $datos->{usuarios}->@*;
my @roles    = $datos->{usuarios}[0]{roles}->@*;
my %primero  = $datos->{usuarios}[0]->%*;

# Mal: Desreferenciación circunfija (más difícil de leer en cadenas)
my @usuarios = @{ $datos->{usuarios} };
my @roles    = @{ $datos->{usuarios}[0]{roles} };

5. El Operador isa (5.32+)

Verificación de tipo infija — reemplaza blessed($o) && $o->isa('X').

use v5.36;
if ($obj isa 'Mi::Clase') { $obj->hacer_algo }

Manejo de Errores

Patrón eval/die

use v5.36;

sub parsear_config($ruta) {
    my $contenido = eval { path($ruta)->slurp_utf8 };
    die "Error de configuración: $@" if $@;
    return decode_json($contenido);
}

Try::Tiny (Manejo Confiable de Excepciones)

use v5.36;
use Try::Tiny;

sub obtener_usuario($id) {
    my $usuario = try {
        $db->resultset('User')->find($id)
            // die "Usuario $id no encontrado\n";
    }
    catch {
        warn "Error al obtener usuario $id: $_";
        undef;
    };
    return $usuario;
}

try/catch Nativo (5.40+)

use v5.40;

sub dividir($x, $y) {
    try {
        die "División por cero" if $y == 0;
        return $x / $y;
    }
    catch ($e) {
        warn "Error: $e";
        return;
    }
}

POO Moderna con Moo

Prefiere Moo para POO ligera y moderna. Usa Moose solo cuando su metaprotocolo sea necesario.

# Bien: Clase Moo
package Usuario;
use Moo;
use Types::Standard qw(Str Int ArrayRef);
use namespace::autoclean;

has nombre  => (is => 'ro', isa => Str, required => 1);
has correo  => (is => 'ro', isa => Str, required => 1);
has edad    => (is => 'ro', isa => Int, default  => sub { 0 });
has roles   => (is => 'ro', isa => ArrayRef[Str], default => sub { [] });

sub es_admin($self) {
    return grep { $_ eq 'admin' } $self->roles->@*;
}

sub saludar($self) {
    return "Hola, soy " . $self->nombre;
}

1;

# Uso
my $usuario = Usuario->new(
    nombre => 'Alicia',
    correo => '[email protected]',
    roles  => ['admin', 'usuario'],
);

# Mal: Hashref bendecido (sin validación, sin acceso)
package Usuario;
sub new {
    my ($clase, %args) = @_;
    return bless \%args, $clase;
}
sub nombre { return $_[0]->{nombre} }
1;

Roles con Moo

package Rol::Serializable;
use Moo::Role;
use JSON::MaybeXS qw(encode_json);
requires 'TO_HASH';
sub to_json($self) { encode_json($self->TO_HASH) }
1;

package Usuario;
use Moo;
with 'Rol::Serializable';
has nombre => (is => 'ro', required => 1);
has correo => (is => 'ro', required => 1);
sub TO_HASH($self) { { nombre => $self->nombre, correo => $self->correo } }
1;

Palabra Clave class Nativa (5.38+, Corinna)

use v5.38;
use feature 'class';
no warnings 'experimental::class';

class Punto {
    field $x :param;
    field $y :param;
    method magnitud() { sqrt($x**2 + $y**2) }
}

my $p = Punto->new(x => 3, y => 4);
say $p->magnitud;  # 5

Expresiones Regulares

Capturas Nombradas y Bandera /x

use v5.36;

# Bien: Capturas nombradas con /x para legibilidad
my $re_log = qr{
    ^ (?<marca_tiempo> \d{4}-\d{2}-\d{2} \s \d{2}:\d{2}:\d{2} )
    \s+ \[ (?<nivel> \w+ ) \]
    \s+ (?<mensaje> .+ ) $
}x;

if ($linea =~ $re_log) {
    say "Hora: $+{marca_tiempo}, Nivel: $+{nivel}";
    say "Mensaje: $+{mensaje}";
}

# Mal: Capturas posicionales (difíciles de mantener)
if ($linea =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+\[(\w+)\]\s+(.+)$/) {
    say "Hora: $1, Nivel: $2";
}

Patrones Precompilados

use v5.36;

# Bien: Compilar una vez, usar muchas veces
my $re_correo = qr/^[A-Za-z0-9._%+-]+\@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;

sub validar_correos(@correos) {
    return grep { $_ =~ $re_correo } @correos;
}

Estructuras de Datos

Referencias y Acceso Profundo Seguro

use v5.36;

# Referencias a hash y array
my $config = {
    base_de_datos => {
        host    => 'localhost',
        puerto  => 5432,
        opciones => ['utf8', 'sslmode=require'],
    },
};

# Acceso profundo seguro (devuelve undef si falta algún nivel)
my $puerto  = $config->{base_de_datos}{puerto};           # 5432
my $ausente = $config->{cache}{host};                      # undef, sin error

# Rebanadas de hash
my %subconjunto;
@subconjunto{qw(host puerto)} = @{$config->{base_de_datos}}{qw(host puerto)};

# Rebanadas de array
my @primeros_dos = $config->{base_de_datos}{opciones}->@[0, 1];

# Bucle for con múltiples variables (experimental en 5.36, estable en 5.40)
use feature 'for_list';
no warnings 'experimental::for_list';
for my ($clave, $valor) (%$config) {
    say "$clave => $valor";
}

E/S de Archivos

Open de Tres Argumentos

use v5.36;

# Bien: Open de tres argumentos con autodie (módulo del núcleo, elimina 'or die')
use autodie;

sub leer_archivo($ruta) {
    open my $fh, '<:encoding(UTF-8)', $ruta;
    local $/;
    my $contenido = <$fh>;
    close $fh;
    return $contenido;
}

# Mal: Open de dos argumentos (riesgo de inyección de comandos, ver perl-security)
open FH, $ruta;            # NUNCA hagas esto
open FH, "< $ruta";        # Sigue siendo malo: datos de usuario en la cadena de modo

Path::Tiny para Operaciones con Archivos

use v5.36;
use Path::Tiny;

my $archivo = path('config', 'app.json');
my $contenido = $archivo->slurp_utf8;
$archivo->spew_utf8($nuevo_contenido);

# Iterar directorio
for my $hijo (path('src')->children(qr/\.pl$/)) {
    say $hijo->basename;
}

Organización de Módulos

Diseño Estándar de Proyecto

MiApp/
├── lib/
│   └── MiApp/
│       ├── App.pm           # Módulo principal
│       ├── Config.pm        # Configuración
│       ├── DB.pm            # Capa de base de datos
│       └── Util.pm          # Utilidades
├── bin/
│   └── miapp                # Script de entrada
├── t/
│   ├── 00-load.t            # Pruebas de compilación
│   ├── unit/                # Pruebas unitarias
│   └── integration/         # Pruebas de integración
├── cpanfile                 # Dependencias
├── Makefile.PL              # Sistema de construcción
└── .perlcriticrc            # Configuración de linting

Patrones de Exportación

package MiApp::Util;
use v5.36;
use Exporter 'import';

our @EXPORT_OK   = qw(recortar);
our %EXPORT_TAGS = (todas => \@EXPORT_OK);

sub recortar($str) { $str =~ s/^\s+|\s+$//gr }

1;

Herramientas

Configuración de perltidy (.perltidyrc)

-i=4        # Sangría de 4 espacios
-l=100      # Longitud de línea de 100 caracteres
-ci=4       # Sangría de continuación
-ce         # else acurrucado
-bar        # Llave de apertura en la misma línea
-nolq       # No sangrar cadenas largas con comillas

Configuración de perlcritic (.perlcriticrc)

severity = 3
theme = core + pbp + security

[InputOutput::RequireCheckedSyscalls]
functions = :builtins
exclude_functions = say print

[Subroutines::ProhibitExplicitReturnUndef]
severity = 4

[ValuesAndExpressions::ProhibitMagicNumbers]
allowed_values = 0 1 2 -1

Gestión de Dependencias (cpanfile + carton)

cpanm App::cpanminus Carton   # Instalar herramientas
carton install                 # Instalar dependencias desde cpanfile
carton exec -- perl bin/miapp  # Ejecutar con dependencias locales
# cpanfile
requires 'Moo', '>= 2.005';
requires 'Path::Tiny';
requires 'JSON::MaybeXS';
requires 'Try::Tiny';

on test => sub {
    requires 'Test2::V0';
    requires 'Test::MockModule';
};

Referencia Rápida: Modismos Modernos de Perl

Patrón HeredadoReemplazo Moderno
use strict; use warnings;use v5.36;
my ($x, $y) = @_;sub foo($x, $y) { ... }
@{ $ref }$ref->@*
%{ $ref }$ref->%*
open FH, "< $archivo"open my $fh, '<:encoding(UTF-8)', $archivo
hashref bendecidoClase Moo con tipos
$1, $2, $3$+{nombre} (capturas nombradas)
eval { }; if ($@)Try::Tiny o try/catch nativo (5.40+)
BEGIN { require Exporter; }use Exporter 'import';
Operaciones manuales de archivoPath::Tiny
blessed($o) && $o->isa('X')$o isa 'X' (5.32+)
builtin::true / falseuse builtin 'true', 'false'; (5.36+, experimental)

Antipatrones

# 1. Open de dos argumentos (riesgo de seguridad)
open FH, $nombre_archivo;                     # NUNCA

# 2. Sintaxis de objeto indirecto (análisis ambiguo)
my $obj = new Foo(bar => 1);                  # Mal
my $obj = Foo->new(bar => 1);                 # Bien

# 3. Dependencia excesiva de $_
map { procesar($_) } grep { validar($_) } @items;  # Difícil de seguir
my @validos = grep { validar($_) } @items;          # Mejor: dividir
my @resultados = map { procesar($_) } @validos;

# 4. Deshabilitar strict refs
no strict 'refs';                            # Casi siempre está mal
${"Mi::Paquete::$variable"} = $valor;        # Usa un hash en su lugar

# 5. Variables globales como configuración
our $TIEMPO_ESPERA = 30;                     # Mal: global mutable
use constant TIEMPO_ESPERA => 30;            # Mejor: constante
# Mejor aún: atributo Moo con valor por defecto

# 6. eval de cadena para cargar módulos
eval "require $modulo";                     # Mal: riesgo de inyección de código
eval "use $modulo";                         # Mal
use Module::Runtime 'require_module';        # Bien: carga segura de módulos
require_module($modulo);

Recuerda: El Perl moderno es limpio, legible y seguro. Deja que use v5.36 maneje el código repetitivo, usa Moo para objetos y prefiere los módulos probados de CPAN sobre soluciones hechas a mano.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Skills relacionados