<?xml version="1.0" encoding="windows-1252"?>
<node id="418891" title="Writing Solid CPAN Modules" created="2005-01-03 03:28:51" updated="2005-08-15 10:16:48">
<type id="120">
perlmeditation</type>
<author id="176576">
eyepopslikeamosquito</author>
<data>
<field name="doctext">
&lt;P&gt;
Writing a rock-solid general-purpose CPAN module is hard.
Very hard. After all, such modules are expected to work flawlessly
in a wide variety of environments -- many of which the author may
have no experience in.
&lt;/P&gt;

&lt;P&gt;
To illustrate how hard it can be to write a module that works in
many different environments, consider two recent examples.
Schwern gushes
&lt;a href="http://nntp.perl.org/group/perl.qa/3229"&gt;here&lt;/a&gt;
about the joy he derives from coaxing his
lovingly crafted Test::More module to work faultlessly in multi-threaded
environments -- even though he never uses threads himself.
And in [id://414238], [Ovid] reminds us that writing a CPAN module that
works smoothly in a
&lt;a href="http://apache.perl.org/"&gt;mod_perl&lt;/a&gt;
environment is not trivial.
&lt;/P&gt;

&lt;P&gt;
To help those CPAN authors less experienced than an Ovid or a Schwern,
I've tried to provide some tips and links on improving CPAN module
quality.
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;Choosing a Module Name&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
The
&lt;a href="http://www.cpan.org/modules/00modlist.long.html"&gt;Pause Module List&lt;/a&gt;
gives some excellent advice which I won't repeat here.
I will offer a word of warning, however: if you trample on
the global namespace, you will be flamed!
Two recent attacks that spring to mind are:
&lt;a href="http://cpanratings.perl.org/a/3860"&gt;Unix-0.02&lt;/a&gt;
where the name &lt;I&gt;Unix&lt;/I&gt; was chosen not because the module had
anything to do with Unix, but because it was "Unix inspired"
(fanning the flames, the author then published a rebuttal of the criticisms
in his module's POD ... which led to more flames for perverted use of POD);
and
&lt;a href="http://search.cpan.org/dist/Acme-Util/"&gt;Util&lt;/a&gt;
where the author, in a desperate attempt to flee the relentless
cpanrating flames, moved his module from the global to the Acme namespace
(though clearly it does not belong in either).
So, save yourself a lot of pain and discuss your module name
in a public forum (typically the module-authors@perl.org mailing list)
well before you upload it to the CPAN.
If you are introducing a new module in an area where others exist,
please take the time to clearly describe how your module differs
from them and why you wrote it.
&lt;/P&gt;

&lt;P&gt;
The most relevant piece of naming advice from Perl Best Practices is practice 3.1: &lt;I&gt;"Use grammatical templates when forming identifiers"&lt;/I&gt;. For packages and classes, a suitable template is:
&lt;blockquote&gt;
&lt;code&gt;
Abstract_noun
Abstract_noun::Adjective
Abstract_noun::Adjective1::Adjective2
&lt;/code&gt;
&lt;/blockquote&gt;
For example:
&lt;blockquote&gt;
&lt;code&gt;
package Disk;
package Disk::Audio;
package Disk::DVD;
package Disk::DVD::Rewritable
&lt;/code&gt;
&lt;/blockquote&gt;
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;Module Reviews&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
If you're lucky, your module might be reviewed at
&lt;a href="http://cpanratings.perl.org/"&gt;cpan ratings&lt;/a&gt;
or
&lt;a href="http://cpan.thegav.com/"&gt;gav's CPAN wiki&lt;/a&gt;
or
&lt;a href="http://www.perladvent.org/"&gt;Mark Fowler's lovely Advent Calendar&lt;/a&gt;
or
&lt;a href="http://neilb.org/reviews/"&gt;Neil Bowers CPAN Module Reviews&lt;/a&gt; (2012 update)
or even here in the Perl Monks [Module Reviews] section.
You might even try posting a request for review at
&lt;a href="http://lists.netthink.co.uk/listinfo/code-review-ladder"&gt;Simon's code review ladder&lt;/a&gt;.
&lt;B&gt;Update Oct 2011:&lt;/B&gt; A new &lt;a href="http://blogs.perl.org/users/kentaro/2011/10/introducing-to-prepan.html"&gt;PrePAN&lt;/a&gt; module review site is now up.
However, you're unlikely to gain much from these sources simply
because performing a detailed, quality module review is &lt;I&gt;very&lt;/I&gt; time
consuming and few people have the time and inclination to do it.
&lt;/P&gt;

&lt;P&gt;
A more practical alternative is to isolate small pieces of code from
the module that you're unhappy with and post multiple small questions
to Perl Monks. Much more likely to elicit a response than
posting a 1000-line module for review.
&lt;/P&gt;

&lt;P&gt;
Another approach is to review your own module, using the checklists below.
When reviewing your own module, it's helpful to perform several reviews,
each one from a different perspective: beginner user perspective,
expert user perspective, maintenance programmer perspective,
support analyst perspective, and so on.
Many programmers (including me) don't pay enough attention to the
customer view of the system. I've found the simple act of pretending
to be a first time user or a support phone jockey uncovers many
ideas for improving quality.
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;Module Review Checklist&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
I'll start by listing the general areas that a module review
might cover; those areas that I have an interest in,
I'll discuss in a bit more detail later.
&lt;/P&gt;

&lt;P&gt;
  &lt;ul&gt;
    &lt;li&gt;Module interface and ease-of-use. See [doc://perlmodstyle] and [id://553487].
    &lt;li&gt;Testability and Test Suite (see next section).
    &lt;li&gt;Documentation. Tutorial and Reference. Examples and Cookbook. Maintainer doco.
        Document how your module is different to similar ones. Change log.
        Notes re portability, performance, bugs, limits, caveats,
        bug reporting, ...
    &lt;li&gt;Stylistic issues: consistency, variable naming, indentation,
        magic numbers, commenting, long subs, global variables, ...
    &lt;li&gt; Review the module with regard to the 256 guidelines in &lt;a href="http://www.oreilly.com/catalog/perlbp/index.html"&gt;Perl Best Practices&lt;/a&gt;.
    &lt;li&gt;Use a tool to test &lt;a href="http://nntp.perl.org/group/perl.qa/149"&gt;kwalitee&lt;/a&gt; (e.g. Perl::Critic, Module::CPANTS, Devel::Cover, Devel::SawAmpersand, Perl::MinimumVersion, Devel::Cycle, Test::Memory::Cycle, lint, valgrind, ...).
    &lt;li&gt;Use common and well-known idioms and programming practices.
    &lt;li&gt;Sniff out &lt;a href="http://c2.com/cgi/wiki?CodeSmell"&gt;code smells&lt;/a&gt;.
    &lt;li&gt;Portability.
    &lt;li&gt;Performance.
    &lt;li&gt;Robustness (see Solidity checklists below).
    &lt;li&gt;Security. See [id://417490], [id://637487] and [pjf]'s excellent perl security course notes, [id://552402].
    &lt;li&gt;Error Handling. Document all errors in the user's dialect. Prefer throwing exceptions to returning special values.
    &lt;li&gt;Are edge cases handled properly?
    &lt;li&gt;Plays well with others. Don't use $&amp;, $', $`,
        don't export anything by default, localize Perl globals, Makefile.PL evilness (e.g. &lt;a href="http://nntp.perl.org/group/perl.modules/31529"&gt;phone home&lt;/a&gt;).
    &lt;li&gt;Avoid code and other duplication.
    &lt;li&gt;Simplicity and Clarity.
    &lt;li&gt;Generality and Extensibility.
    &lt;li&gt;Abstraction and Encapsulation.
    &lt;li&gt;Reuse and Decoupling.
    &lt;li&gt;Maintainability.
    &lt;li&gt;Supportability and Traceability.
  &lt;/ul&gt;
&lt;/P&gt;

&lt;P&gt;
See also [id://744932].
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;Testability and Test Suite&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
Was the test suite written before, during or after the module?
The benefits of
&lt;a href="http://c2.com/cgi/wiki?TestDrivenDevelopment"&gt;Test Driven Development&lt;/a&gt;
are well known and I won't further elaborate here.
&lt;/P&gt;

&lt;P&gt;
Are there nicely commented tests covering the examples in the documentation?
I strongly encourage this because:
it acts as tutorial material for someone browsing the test suite;
and it ensures the examples given in the documentation actually work.
&lt;/P&gt;

&lt;P&gt;
How isolated/independent are the tests? Can they be run in any order? Are boundary conditions tested? Are errors and exceptions tested?
&lt;/P&gt;

&lt;P&gt;
How maintainable is the test suite? How long does it take to run? Is it one monolithic script or broken into a number of smaller ones, one per functional area? Are
&lt;a href="http://www.martinfowler.com/articles/mocksArentStubs.html"&gt;Mocks/Stubs&lt;/a&gt;
employed, where practicable, to test platform-specific features on all platforms?
&lt;/P&gt;

&lt;P&gt;
Test suite code coverage (via Devel::Cover) should be at least 80% in my view.
POD coverage should be 100% and is easily enforced via pod-coverage.t (auto-generated by Module::Starter):
&lt;CODE&gt;
use Test::More;
eval "use Test::Pod::Coverage 0.08";
plan skip_all =&gt; "Test::Pod::Coverage 0.08 required for testing POD coverage" if $@;
all_pod_coverage_ok();
&lt;/CODE&gt;
&lt;/P&gt;

&lt;P&gt;
&lt;B&gt;Update&lt;/B&gt; (2009): Nowadays, you can use &lt;a href="http://search.cpan.org/dist/Test-XT/"&gt;Test::XT&lt;/a&gt; to generate "best practice" author tests. See also this &lt;a href="http://use.perl.org/~Alias/journal/39456"&gt;alias use.perl.org journal&lt;/a&gt;.
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;General Code Solidity Checklist&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
Programming languages, such as C++ and Java, tend to classify routines as:
&lt;/P&gt;

&lt;P&gt;
  &lt;ul&gt;
    &lt;li&gt;unsafe (uses unprotected global or static state)
    &lt;li&gt;thread-safe (aka MT-safe)
    &lt;li&gt;async-signal-safe (reentrant)
    &lt;li&gt;exception-safe
  &lt;/ul&gt;
&lt;/P&gt;

&lt;P&gt;
(There is also async-cancel-safe, which is of little practical importance).
&lt;B&gt;Update:&lt;/B&gt; As pointed out by [BrowserUk] below, the above categories, though important when writing C extensions, are mostly irrelevant at the Perl level. I'd be interested if anyone could provide examples of where the above categories are relevant when writing pure Perl code.
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;Thread Safety&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;&lt;small&gt;Update: please see [BrowserUk]'s response below for thread safety advice at the Perl level.&lt;/small&gt;&lt;/P&gt;

&lt;P&gt;
It's not easy to determine if a piece of code is thread safe.
Nor is it easy to write tests to prove its thread safety.
However, armed with an understanding of
&lt;a href="http://en.wikipedia.org/wiki/Thread-safe"&gt;thread safety&lt;/a&gt;,
a careful examination of the code will prove fruitful.
Note that thread safety and reentrancy (aka efficient thread safety)
should be considered early (at the C level) since they often affect
interfaces and are hard to retrofit later.
&lt;/P&gt;

&lt;P&gt;
See [doc://perlthrtut] for information on Perl thread safety.
An interesting twist in making a Perl module thread-safe is working
around core constructs and modules known to be thread-unsafe.
For instance,
Test::More v0.51_01 was changed to use a Perl sort block rather than
a subroutine because sort subroutines are known to be thread-hostile
(perl bug #30333 discussed in [id://367162]).
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;Exception Safety&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
In C++, the dominant exception handling idiom is
&lt;a href="http://www.research.att.com/~bs/glossary.html#Gresource-acquisition-is-initialization"&gt;RAII (Resource Acquisition is Initialization)&lt;/a&gt;,
which I much prefer to the
&lt;a href="http://blogs.msdn.com/hsutter/archive/2004/07/31/203137.aspx"&gt;Java Dispose pattern&lt;/a&gt;.
Though RAII can't be used in full garbage collected languages, such as Java and Perl 6,
it can work well in simple reference counted languages, like Perl 5.
&lt;/P&gt;

&lt;P&gt;To give a very simple example, this code:
&lt;CODE&gt;
sub fred {
    open(FH, "&lt; f.tmp") or die "open error f.tmp";
    # ... process file here
    die "oops";   # if something went wrong
    close(FH);
}
eval { fred() };
if ($@) { print "died: $@\n" }
# oops, handle FH is still open if exception was thrown.
&lt;/CODE&gt;
is not exception-safe because FH is not closed when die is called.
A simple remedy is to replace the global FH with a lexical file
handle (which is auto-closed at end of scope (RAII)):
&lt;CODE&gt;
sub fred {
    open(my $fh, "&lt; f.tmp") or die "open error f.tmp";
    # ... process file here
    die "oops";   # if something went wrong
    close($fh);
}
eval { fred() };
if ($@) { print "died: $@\n" }
# ok, $fh is auto-closed when its ref count goes to zero
&lt;/CODE&gt;
If you are stuck with Perl 5.005 and can't use a lexical file handle,
IO::File or localizing FH with &lt;CODE&gt;local *FH&lt;/CODE&gt; should do the trick.
&lt;/P&gt;

&lt;P&gt;
See also
[id://230799] and
[id://276911] and
&lt;a href="http://perl.apache.org/docs/general/perl_reference/perl_reference.html#Alternative_Exception_Handling_Techniques"&gt;mod_perl exception handling techniques&lt;/a&gt;.
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;Perl-specific Code Solidity Checklist&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
I've taken the liberty of extending the General Code Solidity
Checklist above with some Perl-specific ones:
&lt;/P&gt;

&lt;P&gt;
  &lt;ul&gt;
    &lt;li&gt;strict-safe
    &lt;li&gt;warnings-safe
    &lt;li&gt;taint-safe
    &lt;li&gt;mod_perl-safe
    &lt;li&gt;coverage-safe ([cpan://Devel::Cover], [cpan://Pod::Coverage])
    &lt;li&gt;cpants-safe (keep [cpan://Module::CPANTS] happy)
  &lt;/ul&gt;
&lt;/P&gt;

&lt;P&gt;
Sorry, I couldn't resist the last two. ;-)
As you can see, there are many things to consider when writing solid
Perl code!
&lt;/P&gt;

&lt;P&gt;
Ideally, you should test your taint-safe code both with
&lt;I&gt;and&lt;/I&gt; without taint
because taint mode has been an historical
source of bugs and strange differences in behaviour.
And you can do that easily enough via
the Test::Harness prove command's -T switch.
However, if you specify
&lt;CODE&gt;#!/usr/bin/perl -wT&lt;/CODE&gt; in a test script, &lt;CODE&gt;make test&lt;/CODE&gt;
will run it in taint mode &lt;I&gt;only&lt;/I&gt;
(anyone know of an easy way around this?).
&lt;/P&gt;

&lt;P&gt;
For information on mod_perl safety, see
&lt;a href="http://perl.apache.org/docs/general/perl_reference/perl_reference.html"&gt;mod_perl reference&lt;/a&gt;
and chapter 6 (Coding with mod_perl in Mind) of
&lt;a href="http://modperlbook.org/"&gt;Practical mod_perl&lt;/a&gt; (which is now available online).
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;See Also&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
 &lt;ul&gt;
  &lt;li&gt; [id://879515]
  &lt;li&gt; [id://158999]
  &lt;li&gt; [id://102347]
  &lt;li&gt; [id://431702]
  &lt;li&gt; [id://649854]
  &lt;li&gt; [id://856415]
  &lt;li&gt; [id://948021]
  &lt;li&gt; [id://744932]
  &lt;li&gt; [id://511185]
  &lt;li&gt; [id://903657]
  &lt;li&gt; [id://1011175]
  &lt;li&gt; &lt;a href="http://perltraining.com.au/tips/2008-10-15.html"&gt;Starting a module with Module::Starter&lt;/a&gt; (Perl Training Australia)
  &lt;li&gt; &lt;a href="http://www.amazon.com/Writing-Perl-Modules-CPAN-Tregar/dp/159059018X"&gt;Writing Perl Modules for CPAN&lt;/a&gt; book by Sam Tregar&lt;/a&gt; (also available as free pdf at &lt;a href="http://www.onlineweblibrary.com/E-books/Ebook%20Progrmming/Perl/WritingPerlModulesForCPAN.pdf"&gt;Online Web Library&lt;/a&gt;)
  &lt;li&gt; &lt;a href="http://mathforum.org/~ken/perl_modules.html"&gt;Guide to Creating Perl Modules by Ken Williams&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://world.std.com/~swmcd/steven/perl/module_mechanics.html"&gt;Perl Module Mechanics by Steven McDougall
  &lt;li&gt; &lt;a href="http://www.perl.com/pub/a/2007/08/09/making-perl-modules.html"&gt;Making Perl Reusable with Modules by Andy Sylvester&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://www.ddj.com/web-development/184416174"&gt;Making New Distributions by brian d foy (using Template Toolkit)&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://ali.as/top100/"&gt;CPAN Top 100&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://use.perl.org/~tomhukins/journal/39835"&gt;Tom Hukins "Using CPAN's Toolchain to Improve Your Code" journal&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://miltonkeynes.pm.org/talks/2009/04/tom_hukins_cpan_toolchain.pdf"&gt;Tom Hukins "Using CPAN's Toolchain to Improve Your Code" slides&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://neilb.org/reviews/"&gt;Neil Bowers CPAN Module Reviews&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://blogs.perl.org/users/kentaro/2011/10/introducing-to-prepan.html"&gt;PrePAN&lt;/a&gt;
  &lt;li&gt; [doc://perlmodstyle]
  &lt;li&gt; &lt;a href="http://wiki.cpantesters.org/wiki/CPANAuthorNotes"&gt;CPAN Authors FAQ&lt;/a&gt;
  &lt;li&gt; &lt;a href="https://pause.perl.org/pause/query?ACTION=pause_04about"&gt;About PAUSE&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://blogs.perl.org/users/neilb/2012/12/modules-that-are-candidates-for-helping-out.html"&gt;Identifying CPAN distributions you could help out with&lt;/a&gt;
 &lt;/ul&gt;
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;Related CPAN Modules&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
 &lt;ul&gt;
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Perl-Critic/"&gt;Perl::Critic&lt;/a&gt; Critique Perl source code for best practices
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Devel-Cover/"&gt;Devel::Cover&lt;/a&gt; Code coverage metrics for Perl
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Dist-Zilla/"&gt;Dist::Zilla&lt;/a&gt; CPAN distribution builder
  &lt;li&gt; [dist://Module-Release] Automate Software Releases
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Test-XT/"&gt;Test::XT&lt;/a&gt; Generate best practice author tests
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Pod-Coverage/"&gt;Pod::Coverage&lt;/a&gt; Checks if the documentation of a module is comprehensive
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Module-Starter/"&gt;Module::Starter&lt;/a&gt; A simple starter kit for any module
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Module-Setup/"&gt;Module::Setup&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Module-Install/"&gt;Module::Install&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Module-Build/"&gt;Module::Build&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Devel-SawAmpersand/"&gt;Devel::SawAmpersand&lt;/a&gt; Perl extension querying PL_sawampersand variable
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Perl-MinimumVersion/"&gt;Perl::MinimumVersion&lt;/a&gt; Find a minimum required version of perl for Perl code
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Devel-Cycle/"&gt;Devel::Cycle&lt;/a&gt; Find memory cycles in objects
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Test-Memory-Cycle/"&gt;Test::Memory::Cycle&lt;/a&gt; Check for memory leaks and circular memory references
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Test-Pod-Coverage/"&gt;Test::Pod::Coverage&lt;/a&gt; Check for pod coverage in your distribution
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Module-CPANTS/"&gt;Module::CPANTS&lt;/a&gt; CPAN Module Testing
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Module-CPANTS-ProcessCPAN/"&gt;Module::CPANTS::ProcessCPAN&lt;/a&gt; Generate Kwalitee ratings for the whole CPAN
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Module-CPANTS-Analyse/"&gt;Module::CPANTS::Analyse&lt;/a&gt; Generate Kwalitee ratings for a distribution
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/Perldoc-Server/"&gt;Perldoc::Server&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/CPAN-Mini/"&gt;CPAN::Mini&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/CPAN-Mini-Extract/"&gt;CPAN::Mini::Extract&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/CPAN-Mini-Inject/"&gt;CPAN::Mini::Inject&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/CPAN-Mini-Webserver/"&gt;CPAN::Mini::Webserver&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/CPAN-Mini-Devel/"&gt;CPAN::Mini::Devel&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/CPAN-Mini-FromList/"&gt;CPAN::Mini::FromList&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/CPAN-Mini-Growl/"&gt;CPAN::Mini::Growl&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/CPAN-Mini-Indexed/"&gt;CPAN::Mini::Indexed&lt;/a&gt;
  &lt;li&gt; &lt;a href="http://search.cpan.org/dist/CPAN-Mini-Visit/"&gt;CPAN::Mini::Visit&lt;/a&gt;
 &lt;/ul&gt;
&lt;/P&gt;


&lt;p&gt;&lt;small&gt;Considered: [rinceWind] "Edit: promote to tutorials"&lt;br /&gt;
Unconsidered: [castaway] Keep/Edit/Delete: 12/41/0 - We let the author decide if a node is a tutorial.&lt;/small&gt;&lt;/p&gt;

&lt;P&gt;
&lt;small&gt;
Updated June 2006: Added more recent references (e.g. PBP) and tools (e.g. Perl::Critic). Jan 2008: Added module naming advice from PBP. Aug 2009: Added new "Related CPAN Modules" section. Oct 2011: Added link to PrePAN. Nov 2012: Added link to Neil Bowers CPAN module reviews.
&lt;/small&gt;
&lt;/P&gt;</field>
</data>
</node>
