Communitygithub.com

perl-patterns

Padrões modernos de Perl 5.36+, boas práticas e convenções para construir aplicações Perl robustas e sustentáveis. Use ao escrever ou revisar código Perl moderno (5.36+).

O que é perl-patterns?

Este skill define um conjunto de padrões e boas práticas para desenvolvimento com Perl moderno, especificamente a partir da versão 5.36. Ele serve como um guia de referência e um viés a ser aplicado ao escrever, revisar ou refatorar código Perl. O skill promove o uso do pragma `use v5.36` para substituir a burocracia legada, o uso de assinaturas de sub-rotinas para clareza e verificação automática de aridade, e a sensibilidade a contexto (lista vs. escalar), que é um conceito central do Perl. Ele também aborda a desreferenciação posfixa para estruturas de dados aninhadas, o operador `isa` para verificação de tipo, tratamento de erros moderno (com `eval/die`, Try::Tiny e `try/catch` nativo), orientação a objetos moderna com Moo (e menção ao suporte nativo `class`), expressões regulares com capturas nomeadas, entrada/saída de arquivos segura com `autodie` e Path::Tiny, organização de módulos, ferramentas de linting e formatação, e uma tabela de referência rápida de substituições de padrões legados. O skill é aplicável a qualquer plataforma onde o Perl 5.36+ seja utilizado (linha de comando, servidores web, scripts de automação) e funciona fornecendo exemplos de código copiáveis e diretrizes que devem ser adaptadas ao contexto específico do projeto.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/perl-patterns

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Padrões Modernos de Desenvolvimento Perl

Padrões idiomáticos de Perl 5.36+ e boas práticas para construir aplicações robustas e sustentáveis.

Quando Ativar

  • Escrevendo novo código ou módulos Perl
  • Revisando código Perl para conformidade com expressões idiomáticas
  • Refatorando código Perl legado para padrões modernos
  • Projetando arquitetura de módulos Perl
  • Migrando código pré-5.36 para Perl moderno

Como Funciona

Aplique esses padrões como uma tendência para os padrões modernos do Perl 5.36+: assinaturas, módulos explícitos, tratamento de erros focado e limites testáveis. Os exemplos abaixo devem ser copiados como pontos de partida e, em seguida, ajustados para o aplicativo, a pilha de dependências e o modelo de implantação reais à sua frente.

Princípios Centrais

1. Use o Pragma v5.36

Um único use v5.36 substitui a burocracia antiga e ativa strict, warnings e assinaturas de sub-rotinas.

# Bom: Preâmbulo moderno
use v5.36;

sub cumprimentar($nome) {
    say "Olá, $nome!";
}

# Ruim: Burocracia legada
use strict;
use warnings;
use feature 'say', 'signatures';
no warnings 'experimental::signatures';

sub cumprimentar {
    my ($nome) = @_;
    say "Olá, $nome!";
}

2. Assinaturas de Sub-rotinas

Use assinaturas para clareza e verificação automática de aridade.

use v5.36;

# Bom: Assinaturas com valores padrão
sub conectar_bd($host, $porta = 5432, $timeout = 30) {
    # $host é obrigatório, outros têm padrões
    return DBI->connect("dbi:Pg:host=$host;port=$porta", undef, undef, {
        RaiseError => 1,
        PrintError => 0,
    });
}

# Bom: Parâmetro slurpy para argumentos variáveis
sub log_mensagem($nivel, @detalhes) {
    say "[$nivel] " . join(' ', @detalhes);
}

# Ruim: Desempacotamento manual de argumentos
sub conectar_bd {
    my ($host, $porta, $timeout) = @_;
    $porta    //= 5432;
    $timeout //= 30;
    # ...
}

3. Sensibilidade a Contexto

Entenda o contexto escalar vs. lista — um conceito central do Perl.

use v5.36;

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

my @copia  = @itens;            # Contexto de lista: todos os elementos
my $contagem = @itens;            # Contexto escalar: contagem (5)
say "Itens: " . scalar @itens; # Forçar contexto escalar

4. Desreferenciação Pós-fixa

Use a sintaxe de desreferenciação pós-fixa para legibilidade com estruturas aninhadas.

use v5.36;

my $dados = {
    usuarios => [
        { nome => 'Alice', papeis => ['admin', 'usuario'] },
        { nome => 'Bob',   papeis => ['usuario'] },
    ],
};

# Bom: Desreferenciação pós-fixa
my @usuarios = $dados->{usuarios}->@*;
my @papeis = $dados->{usuarios}[0]{papeis}->@*;
my %primeiro = $dados->{usuarios}[0]->%*;

# Ruim: Desreferenciação circunflexa (mais difícil de ler em cadeias)
my @usuarios = @{ $dados->{usuarios} };
my @papeis = @{ $dados->{usuarios}[0]{papeis} };

5. O Operador isa (5.32+)

Verificação de tipo infixa — substitui blessed($o) && $o->isa('X').

use v5.36;
if ($obj isa 'My::Class') { $obj->fazer_algo }

Tratamento de Erros

Padrão eval/die

use v5.36;

sub parsear_config($caminho) {
    my $conteudo = eval { path($caminho)->slurp_utf8 };
    die "Erro no config: $@" if $@;
    return decode_json($conteudo);
}

Try::Tiny (Tratamento de Exceções Confiável)

use v5.36;
use Try::Tiny;

sub buscar_usuario($id) {
    my $usuario = try {
        $db->resultset('User')->find($id)
            // die "Usuário $id não encontrado\n";
    }
    catch {
        warn "Falha ao buscar usuário $id: $_";
        undef;
    };
    return $usuario;
}

try/catch Nativo (5.40+)

use v5.40;

sub dividir($x, $y) {
    try {
        die "Divisão por zero" if $y == 0;
        return $x / $y;
    }
    catch ($e) {
        warn "Erro: $e";
        return;
    }
}

OO Moderno com Moo

Prefira Moo para OO moderno e leve. Use Moose apenas quando seu metaprotocolo for necessário.

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

has nome  => (is => 'ro', isa => Str, required => 1);
has email => (is => 'ro', isa => Str, required => 1);
has idade => (is => 'ro', isa => Int, default  => sub { 0 });
has papeis => (is => 'ro', isa => ArrayRef[Str], default => sub { [] });

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

sub cumprimentar($self) {
    return "Olá, eu sou " . $self->nome;
}

1;

# Uso
my $usuario = Usuario->new(
    nome  => 'Alice',
    email => '[email protected]',
    papeis => ['admin', 'usuario'],
);

# Ruim: Blessed hashref (sem validação, sem accessors)
package Usuario;
sub new {
    my ($class, %args) = @_;
    return bless \%args, $class;
}
sub nome { return $_[0]->{nome} }
1;

Roles Moo

package Role::Serializavel;
use Moo::Role;
use JSON::MaybeXS qw(encode_json);
requires 'PARA_HASH';
sub para_json($self) { encode_json($self->PARA_HASH) }
1;

package Usuario;
use Moo;
with 'Role::Serializavel';
has nome  => (is => 'ro', required => 1);
has email => (is => 'ro', required => 1);
sub PARA_HASH($self) { { nome => $self->nome, email => $self->email } }
1;

Palavra-chave class Nativa (5.38+, Corinna)

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

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

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

Expressões Regulares

Capturas Nomeadas e Sinalizador /x

use v5.36;

# Bom: Capturas nomeadas com /x para legibilidade
my $log_re = qr{
    ^ (?<timestamp> \d{4}-\d{2}-\d{2} \s \d{2}:\d{2}:\d{2} )
    \s+ \[ (?<nivel> \w+ ) \]
    \s+ (?<mensagem> .+ ) $
}x;

if ($linha =~ $log_re) {
    say "Hora: $+{timestamp}, Nível: $+{nivel}";
    say "Mensagem: $+{mensagem}";
}

# Ruim: Capturas posicionais (difícil de manter)
if ($linha =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+\[(\w+)\]\s+(.+)$/) {
    say "Hora: $1, Nível: $2";
}

Padrões Pré-compilados

use v5.36;

# Bom: Compile uma vez, use muitas
my $email_re = qr/^[A-Za-z0-9._%+-]+\@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;

sub validar_emails(@emails) {
    return grep { $_ =~ $email_re } @emails;
}

Estruturas de Dados

Referências e Acesso Seguro a Dados Profundos

use v5.36;

# Referências a hash e array
my $config = {
    database => {
        host => 'localhost',
        porta => 5432,
        opcoes => ['utf8', 'sslmode=require'],
    },
};

# Acesso seguro a dados profundos (retorna undef se qualquer nível estiver faltando)
my $porta = $config->{database}{porta};           # 5432
my $faltando = $config->{cache}{host};           # undef, nenhum erro

# Fatias de hash
my %subconjunto;
@subconjunto{qw(host porta)} = @{$config->{database}}{qw(host porta)};

# Fatias de array
my @primeiros_dois = $config->{database}{opcoes}->@[0, 1];

# Loop for com múltiplas variáveis (experimental em 5.36, estável em 5.40)
use feature 'for_list';
no warnings 'experimental::for_list';
for my ($chave, $valor) (%$config) {
    say "$chave => $valor";
}

Entrada/Saída de Arquivos

Open de Três Argumentos

use v5.36;

# Bom: open de três argumentos com autodie (módulo central, elimina 'or die')
use autodie;

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

# Ruim: open de dois argumentos (risco de injeção de shell, veja perl-security)
open FH, $caminho;            # NUNCA faça isso
open FH, "< $caminho";        # Ainda ruim — dados do usuário na string de modo

Path::Tiny para Operações de Arquivo

use v5.36;
use Path::Tiny;

my $arquivo = path('config', 'app.json');
my $conteudo = $arquivo->slurp_utf8;
$arquivo->spew_utf8($novo_conteudo);

# Iterar diretório
for my $item (path('src')->children(qr/\.pl$/)) {
    say $item->basename;
}

Organização de Módulos

Layout Padrão de Projeto

MyApp/
├── lib/
│   └── MyApp/
│       ├── App.pm           # Módulo principal
│       ├── Config.pm        # Configuração
│       ├── DB.pm            # Camada de banco de dados
│       └── Util.pm          # Utilitários
├── bin/
│   └── myapp                # Script de entrada
├── t/
│   ├── 00-load.t            # Testes de compilação
│   ├── unit/                # Testes unitários
│   └── integration/         # Testes de integração
├── cpanfile                 # Dependências
├── Makefile.PL              # Sistema de build
└── .perlcriticrc            # Configuração de linting

Padrões Exporter

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

our @EXPORT_OK   = qw(trim);
our %EXPORT_TAGS = (all => \@EXPORT_OK);

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

1;

Ferramentas

Configuração do perltidy (.perltidyrc)

-i=4        # Indentação de 4 espaços
-l=100      # Comprimento de linha de 100 caracteres
-ci=4       # Indentação de continuação
-ce         # cuddled else
-bar        # Chave de abertura na mesma linha
-nolq       # Não recuar strings longas entre aspas

Configuração do 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

Gerenciamento de Dependências (cpanfile + carton)

cpanm App::cpanminus Carton   # Instalar ferramentas
carton install                 # Instalar dependências do cpanfile
carton exec -- perl bin/myapp  # Executar com dependências locais
# cpanfile
requires 'Moo', '>= 2.005';
requires 'Path::Tiny';
requires 'JSON::MaybeXS';
requires 'Try::Tiny';

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

Referência Rápida: Expressões Idiomáticas Modernas do Perl

Padrão LegadoSubstituição Moderna
use strict; use warnings;use v5.36;
my ($x, $y) = @_;sub foo($x, $y) { ... }
@{ $ref }$ref->@*
%{ $ref }$ref->%*
open FH, "< $arquivo"open my $fh, '<:encoding(UTF-8)', $arquivo
blessed hashrefClasse Moo com tipos
$1, $2, $3$+{nome} (capturas nomeadas)
eval { }; if ($@)Try::Tiny ou try/catch nativo (5.40+)
BEGIN { require Exporter; }use Exporter 'import';
Operações manuais de arquivoPath::Tiny
blessed($o) && $o->isa('X')$o isa 'X' (5.32+)
builtin::true / falseuse builtin 'true', 'false'; (5.36+, experimental)

Anti-padrões

# 1. open de dois argumentos (risco de segurança)
open FH, $nome_arquivo;                     # NUNCA

# 2. Sintaxe de objeto indireto (análise ambígua)
my $obj = new Foo(bar => 1);            # Ruim
my $obj = Foo->new(bar => 1);           # Bom

# 3. Dependência excessiva de $_
map { processar($_) } grep { validar($_) } @itens;  # Difícil de acompanhar
my @validos = grep { validar($_) } @itens;           # Melhor: dividir
my @resultados = map { processar($_) } @validos;

# 4. Desabilitar strict refs
no strict 'refs';                        # Quase sempre errado
${"My::Package::$var"} = $valor;         # Use um hash

# 5. Variáveis globais como configuração
our $TIMEOUT = 30;                       # Ruim: global mutável
use constant TIMEOUT => 30;              # Melhor: constante
# Melhor: Atributo Moo com valor padrão

# 6. eval de string para carregar módulos
eval "require $modulo";                  # Ruim: risco de injeção de código
eval "use $modulo";                      # Ruim
use Module::Runtime 'require_module';    # Bom: carregamento seguro de módulos
require_module($modulo);

Lembre-se: Perl moderno é limpo, legível e seguro. Deixe use v5.36 lidar com a burocracia, use Moo para objetos e prefira os módulos testados em batalha do CPAN em vez de soluções feitas à mão.

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/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

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.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

Habilidades Relacionadas