| name | perl-core |
| description | Getty's house rules for all Perl code — module loading, Moose patterns, cpanfile versioning for Getty-authored CPAN distributions, stylistic choices that differ from defaults. Load on any Perl edit in a Getty project. |
Perl Core — Getty House Rules
These rules override defaults. They are non-negotiable in Getty projects.
Module loading
use Module; at the top. Always. Every dependency is loaded at compile time.
require is forbidden as a "lazy optimization". Never use it to shave startup. If you find yourself writing require Foo; inside a method body, stop. Move it to a top-level use.
require is allowed ONLY for true runtime plugin loading — i.e. the class to load is determined from config/DB at runtime (e.g. Module::Runtime::use_module($class_from_db)). If the class name is known at write-time, use it.
require + ->new directly in a controller action is a red flag. Fix by hoisting to use at the top of the file.
Singletons
->instance for classes that use MooseX::Singleton or MooX::Singleton. Never call ->new on a singleton.
->new for everything else.
Getty CPAN distributions — cpanfile versioning
Getty's dist.ini uses [@Author::GETTY], which sets $VERSION in the repo to the next, unreleased version (e.g. 0.402 while CPAN is at 0.401). The repo is ALWAYS ahead of CPAN by one.
Rules:
- NEVER copy a version number from a Getty-authored repo into a
cpanfile. The repo version is not released. cpanm cannot install it. Your build will break.
- Check
cpanm --info Module::Name (or CPAN / MetaCPAN) to get the actual released version.
- Every Getty-authored distribution listed in our project cpanfiles must be pinned to the latest released version on CPAN. Not
'0', not some stale number — the current latest. Check with cpanm --info before writing the requires line.
- Re-check on upgrade. When bumping, use
cpanm --info again.
Getty-authored examples (non-exhaustive): Langertha, IO::K8s, Kubernetes::REST, WWW::Firecrawl, Net::Async::Firecrawl, Net::Async::WebSearch, Catalyst::Plugin::ChainedURI, Locale::Simple, DBIO::*, WWW::Zitadel, WWW::PayPal, WWW::Chain.
Quick check for unknown modules:
cpanm --info Module::Name | tail -1
Moose / OOP style
lazy_build => 1 + sub _build_foo is strongly preferred over default => sub { ... } for anything non-trivial. Keeps attribute declarations clean.
weak_ref => 1 on attributes that hold a reference back to a parent/owner object. Standard for nested Moose object graphs — prevents circular refs.
namespace::autoclean on every class file. For classes that extend DBIx::Class (MooseX::NonMoose pattern), use MooseX::MarkAsMethods autoclean => 1 instead.
no Moose; + __PACKAGE__->meta->make_immutable; at the bottom of every Moose class.
my ( $self ) = @_; — explicit destructure, not my $self = shift;. Space inside the parens.
- Explicit import lists with
qw( ... ) — use Foo qw( bar baz );. Never rely on default exports unless they're documented as stable.
Style / whitespace
- 2-space indentation. Not 4. Not tabs. Every Getty Perl file.
- No trailing commas at the end of multi-line lists (different from Python convention).
File I/O
Path::Tiny for every file operation. Not File::Spec, not bare open. Method-chain: path(...)->child(...)->slurp_utf8.
JSON
JSON::MaybeXS always. When encoding, set canonical => 1, convert_blessed => 1 on the encoder object.
Forbidden / anti-patterns
- ❌
require Foo inside a method to "speed up startup"
- ❌ Using
$VERSION from a Getty repo as the cpanfile requirement
- ❌
default => sub { ... } for a non-trivial Moose attribute default (use lazy_build)
- ❌ 4-space indent in new Perl files
- ❌
File::Spec in new code
- ❌
Data::Dumper in shipped code (use DDP / Data::Printer for debug, strip before commit)
When in doubt
Grep an existing Getty project (~/dev/perl/ellis/edge/, ~/dev/perl/langertha/, ~/dev/perl/shore/) for how the pattern is used there. That is the ground truth.