QUALITY
* Named parameters to subroutines, passed as a hashref. Some cases of subroutines taking only one or two positional parameters may be acceptable, if it's obvious what they should be.
* Parameters to subroutines are validated (e.g. Params::Validate, MooseX::Params::Validate, Type::Tiny with Moo
* use strict; use warnings; # or equivalent (e.g. Moose or NAP::policy) for every file. A test to ensure this, e.g. Perl::Critic's RequireUseStrict and RequireUseWarnings.
* Does not use the Switch module, the bugs in that module are extremely dangerous and even affect code which doesn't appear to use switch at all.
* Well factored - write several subroutines or subclasses with meaningful names and avoid large "if/then" blocks.
* Semantic classes, not utility classes
* Modules have @EXPORT_OK (instead of @EXPORT), so imported methods have to be declared whenever used, and it's easy to trace where they came from
* Variable names are full words with underscores separating words, no abbreviations just "to save typing"
* No magic numbers or magic strings, use constants with comments explaining them. Even better, encapsulate in a method so constants don't have to be defined everywhere.
* Do not re-use variables, i.e. don't use them for different purposes in different parts of the code. Use two variables instead.
* Code written in the language of the problem domain (e.g. $album->{artist}) and not the solution domain (e.g. $hash->{lookup_field}).
* Never perform an eval without checking the error and re-throwing the exception if it's not recognised
* Code written to be easily understandable by other developers. No cleverness or "magic" without detailed explanatory comments.
* Logging via at least one abstraction layer so log output can be easily controlled. Ideally use Log::Any which implements the Observer pattern, so you don't have to pass a log object into your classes.
DOCUMENTATION
* Comments to explain the intention of every distinct section of the code
* At least a few words of POD for _all_ classes and methods explaining the reason for existence, and what it represents in the real world
* POD for functional tests (not needed for unit tests)
* Everything built with a developer in mind who has never seen the system before (there will be many of these in the software's future).
TESTS
* Test suite can run anywhere (checkout dir, dev env, test env, etc.)
* Test suite not brittle, tests pass when run in any order
* Tests clearly separated into:
* environment - if these fail, bail out
* unit tests - only test one module, with dependencies mocked out
* integration tests - tests involving multiple in-team systems
* monitoring - "tests" of external systems - not really tests but a form of monitoring
* Appropriate mix of unit and integration tests
* Tests for POD, syntax, etc. even if with a blacklist
* No logic in the templates, proper MVC separation
* No logic in scripts, only in re-usable modules
FORMATTING
* No mixed tabs and spaces
* Indent with 4 spaces
* Consistent indenting
* No indenting for longer than about 50 lines. If that happens, break some of the logic out into a different subroutine.
The opinions above are my own.
A nod of the head to Damian Conway's "Perl Best Practices".