Communitygithub.com

perl-patterns

Moderne Perl 5.36+ Idiome, Best Practices und Konventionen für robuste, wartbare Perl-Anwendungen. Verwenden Sie dies beim Schreiben oder Überprüfen von modernem Perl 5.36+ Code.

Was ist perl-patterns?

Diese Anleitung beschreibt moderne Perl-Entwicklungsmuster, die auf Perl 5.36+ und neueren Versionen basieren. Sie zielt darauf ab, Entwicklern zu helfen, robuste, wartbare und idiomatische Perl-Anwendungen zu schreiben. Der Fokus liegt auf klaren, lesbaren und sicheren Codierungspraktiken, die die neuesten Sprachfunktionen nutzen. Die Anleitung deckt eine breite Palette von Themen ab, darunter die Verwendung des `v5.36`-Pragmas, Subroutinen-Signaturen, Kontextsensitivität, Postfix-Dereferenzierung sowie den `isa`-Operator. Sie behandelt auch die Fehlerbehandlung mit `eval/die`, `Try::Tiny` und nativen `try/catch`-Blöcken (ab 5.40). Weitere wichtige Bereiche sind modernes OO mit `Moo` und dem nativen `class`-Schlüsselwort (Corinna ab 5.38), reguläre Ausdrücke mit benannten Captures, Datenstruktur-Zugriffsmuster, Datei-I/O mit `Path::Tiny` und Modulorganisation. Für Entwickler, die diesen Skill in KI-Agenten wie Claude Code nutzen, bietet er eine sofort anwendbare Referenz für Code-Reviews, Neuschreibungen oder das Erstellen neuer Perl-Module. Die Anleitung enthält klare Code-Beispiele, die „gute“ von „schlechten“ Praktiken unterscheiden, sowie eine praktische Referenztabelle zur schnellen Konvertierung alter Idiome in moderne Äquivalente. Sie unterstützt gängige Plattformen wie Claude Code, Cursor und Codex und ist als Markdown-Datei strukturiert, die in KI-Prompts oder Skill-Bibliotheken eingebettet werden kann.

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

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Moderne Perl-Entwicklungsmuster

Idiomatische Perl 5.36+ Muster und Best Practices für den Bau robuster, wartbarer Anwendungen.

Wann aktivieren

  • Beim Schreiben von neuem Perl-Code oder Modulen
  • Beim Überprüfen von Perl-Code auf Idiom-Konformität
  • Beim Refactoring von Legacy-Perl auf moderne Standards
  • Beim Entwurf der Perl-Modul-Architektur
  • Beim Migrieren von Code vor 5.36 zu modernem Perl

Funktionsweise

Wenden Sie diese Muster als Voreinstellung für moderne Perl 5.36+ Standardeinstellungen an: Signaturen, explizite Module, fokussierte Fehlerbehandlung und testbare Grenzen. Die folgenden Beispiele sollen als Ausgangspunkte kopiert und dann für die tatsächliche Anwendung, den Abhängigkeitsstapel und das Bereitstellungsmodell angepasst werden.

Kernprinzipien

1. Das v5.36 Pragma verwenden

Ein einzelnes use v5.36 ersetzt den alten Boilerplate-Code und aktiviert strict, warnings und Subroutinen-Signaturen.

# Gut: Moderner Vorspann
use v5.36;

sub greet($name) {
    say "Hallo, $name!";
}

# Schlecht: Legacy-Boilerplate
use strict;
use warnings;
use feature 'say', 'signatures';
no warnings 'experimental::signatures';

sub greet {
    my ($name) = @_;
    say "Hallo, $name!";
}

2. Subroutinen-Signaturen

Verwenden Sie Signaturen für Klarheit und automatische Überprüfung der Argumentanzahl.

use v5.36;

# Gut: Signaturen mit Standardwerten
sub connect_db($host, $port = 5432, $timeout = 30) {
    # $host ist erforderlich, andere haben Standardwerte
    return DBI->connect("dbi:Pg:host=$host;port=$port", undef, undef, {
        RaiseError => 1,
        PrintError => 0,
    });
}

# Gut: Slurpy-Parameter für variable Argumente
sub log_message($level, @details) {
    say "[$level] " . join(' ', @details);
}

# Schlecht: Manuelles Auspacken der Argumente
sub connect_db {
    my ($host, $port, $timeout) = @_;
    $port    //= 5432;
    $timeout //= 30;
    # ...
}

3. Kontextsensitivität

Verstehen Sie skalaren vs. Listenkontext – ein zentrales Perl-Konzept.

use v5.36;

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

my @copy  = @items;            # Listenkontext: alle Elemente
my $count = @items;            # Skalarer Kontext: Anzahl (5)
say "Artikel: " . scalar @items; # Erzwingen des skalaren Kontexts

4. Postfix-Dereferenzierung

Verwenden Sie die Postfix-Dereferenzierungssyntax für bessere Lesbarkeit bei verschachtelten Strukturen.

use v5.36;

my $data = {
    users => [
        { name => 'Alice', roles => ['admin', 'user'] },
        { name => 'Bob',   roles => ['user'] },
    ],
};

# Gut: Postfix-Dereferenzierung
my @users = $data->{users}->@*;
my @roles = $data->{users}[0]{roles}->@*;
my %first = $data->{users}[0]->%*;

# Schlecht: Circumfix-Dereferenzierung (schwerer lesbar in Verkettungen)
my @users = @{ $data->{users} };
my @roles = @{ $data->{users}[0]{roles} };

5. Der isa-Operator (5.32+)

Infix-Typenprüfung – ersetzt blessed($o) && $o->isa('X').

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

Fehlerbehandlung

eval/die-Muster

use v5.36;

sub parse_config($path) {
    my $content = eval { path($path)->slurp_utf8 };
    die "Konfigurationsfehler: $@" if $@;
    return decode_json($content);
}

Try::Tiny (Zuverlässige Ausnahmebehandlung)

use v5.36;
use Try::Tiny;

sub fetch_user($id) {
    my $user = try {
        $db->resultset('User')->find($id)
            // die "Benutzer $id nicht gefunden\n";
    }
    catch {
        warn "Fehler beim Abrufen des Benutzers $id: $_";
        undef;
    };
    return $user;
}

Nativer try/catch (5.40+)

use v5.40;

sub divide($x, $y) {
    try {
        die "Division durch Null" if $y == 0;
        return $x / $y;
    }
    catch ($e) {
        warn "Fehler: $e";
        return;
    }
}

Modernes OO mit Moo

Bevorzugen Sie Moo für leichtgewichtiges, modernes OO. Verwenden Sie Moose nur, wenn sein Metaprotokoll benötigt wird.

# Gut: Moo-Klasse
package User;
use Moo;
use Types::Standard qw(Str Int ArrayRef);
use namespace::autoclean;

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

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

sub greet($self) {
    return "Hallo, ich bin " . $self->name;
}

1;

# Verwendung
my $user = User->new(
    name  => 'Alice',
    email => '[email protected]',
    roles => ['admin', 'user'],
);

# Schlecht: Blessed Hashref (keine Validierung, keine Accessoren)
package User;
sub new {
    my ($class, %args) = @_;
    return bless \%args, $class;
}
sub name { return $_[0]->{name} }
1;

Moo-Rollen

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

package User;
use Moo;
with 'Role::Serializable';
has name  => (is => 'ro', required => 1);
has email => (is => 'ro', required => 1);
sub TO_HASH($self) { { name => $self->name, email => $self->email } }
1;

Nativer class-Schlüsselwort (5.38+, Corinna)

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

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

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

Reguläre Ausdrücke

Benannte Captures und /x-Flag

use v5.36;

# Gut: Benannte Captures mit /x für Lesbarkeit
my $log_re = qr{
    ^ (?<timestamp> \d{4}-\d{2}-\d{2} \s \d{2}:\d{2}:\d{2} )
    \s+ \[ (?<level> \w+ ) \]
    \s+ (?<message> .+ ) $
}x;

if ($line =~ $log_re) {
    say "Zeit: $+{timestamp}, Ebene: $+{level}";
    say "Nachricht: $+{message}";
}

# Schlecht: Positionelle Captures (schwer zu warten)
if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+\[(\w+)\]\s+(.+)$/) {
    say "Zeit: $1, Ebene: $2";
}

Vorkompilierte Muster

use v5.36;

# Gut: Einmal kompilieren, oft verwenden
my $email_re = qr/^[A-Za-z0-9._%+-]+\@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;

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

Datenstrukturen

Referenzen und sicherer tiefer Zugriff

use v5.36;

# Hash- und Array-Referenzen
my $config = {
    database => {
        host => 'localhost',
        port => 5432,
        options => ['utf8', 'sslmode=require'],
    },
};

# Sicherer tiefer Zugriff (gibt undef zurück, wenn eine Ebene fehlt)
my $port = $config->{database}{port};           # 5432
my $missing = $config->{cache}{host};           # undef, kein Fehler

# Hash-Slices
my %subset;
@subset{qw(host port)} = @{$config->{database}}{qw(host port)};

# Array-Slices
my @first_two = $config->{database}{options}->@[0, 1];

# Multi-Variable-for-Schleife (experimentell in 5.36, stabil in 5.40)
use feature 'for_list';
no warnings 'experimental::for_list';
for my ($key, $val) (%$config) {
    say "$key => $val";
}

Datei-I/O

Drei-Argumente-Open

use v5.36;

# Gut: Drei-Argumente-Open mit autodie (Kernmodul, eliminiert 'or die')
use autodie;

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

# Schlecht: Zwei-Argumente-Open (Shell-Injection-Risiko, siehe perl-security)
open FH, $path;            # NIEMALS so machen
open FH, "< $path";        # Immer noch schlecht – Benutzerdaten im Modus-String

Path::Tiny für Dateioperationen

use v5.36;
use Path::Tiny;

my $file = path('config', 'app.json');
my $content = $file->slurp_utf8;
$file->spew_utf8($new_content);

# Verzeichnis durchlaufen
for my $child (path('src')->children(qr/\.pl$/)) {
    say $child->basename;
}

Modulorganisation

Standard-Projektstruktur

MyApp/
├── lib/
│   └── MyApp/
│       ├── App.pm           # Hauptmodul
│       ├── Config.pm        # Konfiguration
│       ├── DB.pm            # Datenbankschicht
│       └── Util.pm          # Hilfsfunktionen
├── bin/
│   └── myapp                # Einstiegspunkt-Skript
├── t/
│   ├── 00-load.t            # Kompilierungstests
│   ├── unit/                # Unit-Tests
│   └── integration/         # Integrationstests
├── cpanfile                 # Abhängigkeiten
├── Makefile.PL              # Build-System
└── .perlcriticrc            # Linting-Konfiguration

Exporter-Muster

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;

Werkzeuge

perltidy-Konfiguration (.perltidyrc)

-i=4        # 4 Leerzeichen Einzug
-l=100      # 100 Zeichen Zeilenlänge
-ci=4       # Fortsetzungseinzug
-ce         # Geschmiegtes else
-bar        # Öffnende Klammer in derselben Zeile
-nolq       # Lange Strings nicht einrücken

perlcritic-Konfiguration (.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

Abhängigkeitsverwaltung (cpanfile + carton)

cpanm App::cpanminus Carton   # Werkzeuge installieren
carton install                 # Abhängigkeiten aus cpanfile installieren
carton exec -- perl bin/myapp  # Mit lokalen Abhängigkeiten ausführen
# cpanfile
requires 'Moo', '>= 2.005';
requires 'Path::Tiny';
requires 'JSON::MaybeXS';
requires 'Try::Tiny';

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

Kurzreferenz: Moderne Perl-Idiome

Legacy-MusterModerne Entsprechung
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 hashrefMoo-Klasse mit Typen
$1, $2, $3$+{name} (benannte Captures)
eval { }; if ($@)Try::Tiny oder nativer try/catch (5.40+)
BEGIN { require Exporter; }use Exporter 'import';
Manuelle DateioperationenPath::Tiny
blessed($o) && $o->isa('X')$o isa 'X' (5.32+)
builtin::true / falseuse builtin 'true', 'false'; (5.36+, experimentell)

Anti-Patterns

# 1. Zwei-Argumente-Open (Sicherheitsrisiko)
open FH, $filename;                     # NIEMALS

# 2. Indirekte Objektsyntax (mehrdeutige Analyse)
my $obj = new Foo(bar => 1);            # Schlecht
my $obj = Foo->new(bar => 1);           # Gut

# 3. Übermäßiges Vertrauen auf $_
map { process($_) } grep { validate($_) } @items;  # Schwer zu folgen
my @valid = grep { validate($_) } @items;           # Besser: aufteilen
my @results = map { process($_) } @valid;

# 4. Deaktivieren von strict refs
no strict 'refs';                        # Fast immer falsch
${"My::Package::$var"} = $value;        # Stattdessen einen Hash verwenden

# 5. Globale Variablen als Konfiguration
our $TIMEOUT = 30;                       # Schlecht: veränderbares Global
use constant TIMEOUT => 30;              # Besser: Konstante
# Am besten: Moo-Attribut mit Standardwert

# 6. String-eval zum Laden von Modulen
eval "require $module";                  # Schlecht: Code-Injection-Risiko
eval "use $module";                      # Schlecht
use Module::Runtime 'require_module';    # Gut: Sicheres Laden von Modulen
require_module($module);

Erinnern Sie sich: Modernes Perl ist sauber, lesbar und sicher. Lassen Sie use v5.36 den Boilerplate-Code erledigen, verwenden Sie Moo für Objekte und bevorzugen Sie die kampferprobten Module von CPAN gegenüber selbst gestrickten Lösungen.

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.

Verwandte Skills