Home / Alt manpages / perlmodstyle(1)

  • perlmodstyle(1)
  • User command
  • linux

Design a Perl Module API That Survives Its First Release

You will leave this guide with a practical review sequence for a Perl module before you publish it: a narrow purpose, a readable interface, strict and warning-free code, usable POD, tests, dependency declarations and a version that can move forward without breaking users. Allow 30 to 45 minutes for a small module, longer if its public API is already in use.

This guide follows the installed perlmodstyle manual from Perl 5.38.2, supplied by Ubuntu's perl-doc package version 5.38.2-3.2ubuntu0.6. It is a style and release checklist, not a module generator. The commands below are ordinary user commands. Installing a module or its prerequisites may need elevated privileges, but that is outside the review itself.

1. Write the module's one-sentence job

Start by stating what the module does in one sentence. The manual's central design test is that a module should do one thing well, with a scope that can be described briefly. If the sentence joins unrelated protocols, output formats or application layers with "and", split the design or explain why the parts cannot sensibly be separate.

Before writing more code, search CPAN and the wider Perl ecosystem for an existing solution. If a close match exists, consider a patch, subclass or extension. This avoids making users choose between two almost identical interfaces and avoids taking responsibility for another copy of a problem that already has a maintained solution.

Purpose: Foo::Report turns records into a structured report for callers to render.

That purpose deliberately says what callers receive, rather than promising that the module will print a particular terminal layout. Keep the module name descriptive, consistent with existing namespaces and based on its function rather than its implementation.

2. Review the public API from the caller's side

Read each public routine as if you did not write the implementation. Simple operations should have simple routines. If one routine changes behaviour radically according to its arguments, separate those behaviours into distinct routines. Prefer returning a Perl data structure over printing text, HTML or a report directly. Callers can then choose their own output format.

Use consistent names for related operations and named parameters when a call has more than a small number of arguments. A hash or hash reference makes the meaning visible and lets you add a parameter without shifting every later positional value:

$report->add_record(
    name => 'wibble',
    type => 'text',
    size => 1024,
);

Lower-case keys are the usual choice for new code. Hyphenated and upper-case keys still exist for historical reasons, but mixing conventions creates needless friction. Supply sensible defaults for common cases and reserve optional parameters for behaviour that genuinely varies. A default is useful only when it is unsurprising and documented.

Decide deliberately whether the module needs object-oriented interfaces, procedural functions or both. OO can fit a large system, aggregated data, polymorphic types or many operations over the same data. It can also make a small task harder to understand. Do not add objects merely because the language supports them.

3. Make failure behaviour predictable

Document what happens when an operation cannot complete. The manual lists several reasonable choices: return an undefined value, expose an error string, warn or carp to standard error, or croak when the module cannot sensibly continue. If you offer configurable warning or debug output, make the common behaviour the default and describe every destination and level.

Do not silently mix return values, printed output and exceptions without a rule. A caller should be able to tell success from failure without parsing a human-oriented report. Test the failure path as well as the happy path, including invalid named parameters and missing input.

Checkpoint: write down one sentence for each public routine covering its inputs, return value and failure signal. If you cannot do that without referring to internal variables, the API is not ready for documentation.

4. Run strict and warning checks

The style guide expects a module to work under use strict and without warnings. Run your normal test command with both enabled, then compile individual module files when investigating a failure:

$ perl -Mstrict -Mwarnings -c lib/Foo/Report.pm
lib/Foo/Report.pm syntax OK
$ prove -lv t

The exact test output depends on your suite. A clean compile is not a substitute for tests: it catches syntax and some compile-time problems, while tests exercise behaviour. Keep taint checking in mind if the module handles data from untrusted sources, but assess it in the context of what the module actually does.

Do not "fix" a warning by suppressing it globally. Find out whether it exposes an undefined value, an accidental string-number conversion or an interface assumption. If a warning is intentional, keep the narrowest documented suppression and test that the reason remains valid.

5. Put the user journey in POD

Write POD for developers who have just installed the module and want to use it. Start with a minimal SYNOPSIS, then describe purpose and scope, followed by the public routines with their parameters and return values. Include a short example that demonstrates the ordinary path. Add caveats, maintainer contact details and links to further information where they help.

A useful order is NAME, SYNOPSIS, DESCRIPTION, detailed routine sections, caveats, author, related material, and licensing. Keep documentation close to the code it describes, particularly when a routine's contract is likely to change. Put tutorials or long background material in separate documents rather than making the main module page difficult to scan.

$ podchecker lib/Foo/Report.pm
Foo/Report.pm pod syntax OK

Also provide a README with the module's purpose and pointers to more information. Keep installation instructions simple. For an ExtUtils::MakeMaker distribution, the usual sequence is perl Makefile.PL, make, make test and, only after review, make install. The install step changes the system or selected Perl library, so do not run it casually or as root.

6. Make releases reproducible

Declare prerequisites in Makefile.PL or Build.PL, and also state the required Perl version in the code with a suitable use or require. Prefer stable core modules where they fit, then stable CPAN dependencies. Test the distribution before publishing, not just the working tree. With MakeMaker, the manual identifies make test for installers and make disttest for distribution-level testing.

Choose a version scheme and keep it stable. The common CPAN-style sequence is two decimal places, such as 1.00, 1.10 and 1.11. Increment the version for every release, including documentation-only changes. If you use an underscore development version such as 1.20_01, follow the documented handling carefully so distribution tools and Perl interpret it as intended.

Before a release, inspect the generated archive, run its tests from a clean location, include a Changes file for user-visible changes, and include the full licence text when its terms require it. Packaging is a release boundary: keep the original working tree and archive available until the result has been checked.

7. Handle compatibility as a promise

If users depend on a stable module, backwards compatibility is part of its interface. Avoid removing or reinterpreting an existing parameter without a transition period. Add new named parameters with defaults where possible. If a breaking change is unavoidable, make the version change and migration path visible rather than hiding the break in a minor release.

Ask for feedback before the first public upload, especially about naming and the problem domain. The module style guide points authors towards experienced authors, related modules and community discussion. That review can catch a namespace collision, an over-broad scope or an API that makes sense only to its implementer.

Done means

  • The module has one clear purpose and a name that matches it.
  • Public routines have consistent names, documented defaults and predictable failure signals.
  • Common calls use understandable parameters, and results are separate from presentation.
  • The code compiles with strict and warnings, and its tests cover success and failure.
  • POD, README, prerequisites, release notes, version changes, tests and licensing are ready for the intended distribution.
  • A clean distribution test has passed, and no install or destructive replacement is being used as a substitute for review.