Write Pod That Parsers Can Read Without Guesswork
Pod will render happily in one formatter and come out wrong in the next, which is what perlpodspec exists to prevent. You will finish with a small Perl documentation file that podchecker accepts, plus a repeatable way to check the parts a formatter cannot infer safely. Pod is usually forgiving enough to render while still being structurally ambiguous; the habits below make the source predictable across HTML, terminal and other formatters.
The route
Jump straight to the step you need, or tick off Done means at the end.
Prerequisites: Perl and the perl-doc package. This guide was checked with Perl v5.38.2 and perl-doc 5.38.2-3.2ubuntu0.6 on Ubuntu. Allow about 15 minutes. You do not need root for the examples.
1. Create a Pod block
Make a scratch file in
/tmp, and put the documentation at the start of a Pod block.cat > /tmp/widget.pod <<'POD' =pod =head1 NAME widget - inspect a widget file =head1 SYNOPSIS widget [OPTIONS] FILE =head1 DESCRIPTION The widget command reads FILE and reports its basic properties. =cut PODCheck the file before adding more content.
podchecker /tmp/widget.podA successful check reports the file has no Pod errors or warnings. A Pod block begins with a line whose first character is
=followed by a letter, and ends at=cutor end of file. A file containing only Pod is valid; Pod can also be embedded inside a Perl source file.
2. Keep paragraph types distinct
Pod decides what a paragraph is from its first line alone. A line beginning with a recognised command creates a command paragraph. A line beginning with a space or tab creates a verbatim paragraph, where whitespace matters. Anything else is an ordinary paragraph. Blank lines separate paragraphs, and spaces or tabs on an otherwise empty line count as blank too.
That first-character rule is an easy place to lose an afternoon. Do not indent =head1, =item or =cut. Do indent literal command examples, because indentation is exactly what makes them verbatim:
=head1 EXAMPLE
printf "hello\n";
This is ordinary paragraph text.
The indented line is documentation text, not a command to be executed. Keep a blank line around it so a later edit does not accidentally merge it with the surrounding prose.
3. Use commands with their intended shape
Use =head1 through =head6 for headings, =over and =back for a list or indented region, and =item for its entries. A bare =over has the same practical default indent as =over 4; a supplied value must be a positive number such as 3 or 3.5.
=head1 OPTIONS
=over 4
=item B<--verbose>
Print additional details.
=item B<--output> I<FILE>
Write the report to FILE.
=back
Choose one list shape and keep it consistent inside a region: bullet items such as =item *, numbered items starting at 1 and continuing without gaps, or descriptive item text. A region with no =item at all is just an indented block. Do not put a heading inside an =over ... =back region.
Commands are not interchangeable. =back takes no trailing text. =end must name the currently open =begin region, in the same nesting order. Treat an unknown command as an error rather than harmless prose, so a typo such as =headd1 gets caught instead of silently rendered as junk.
4. Make formatting codes unambiguous
Formatting codes start with a capital letter. Use I<text> for emphasis, B<text> for bold text, C<code> for code, F<filename> for filenames, and L<...> for links. Escape a literal angle bracket with an E<...> character escape, or use the multiple-angle form when the code itself contains comparison operators.
=head1 EXAMPLE
Call C<open> with a three-argument form:
open my $fh, '<', $path or die $!;
The expression C<$a E<lt>=E<gt> $b> means that $a is compared with $b.
For longer code, multiple angle brackets keep the closing markers visible:
C<<
open my $fh, '<', $path or die $!;
>>
Do not nest L<...> links. For a page and section, write a form such as L<perlfunc/"open">; for an external address, use L<https://example.invalid/>. A section-only link uses L</"OPTIONS">. Supply link text before a vertical bar, as in L<the open documentation|perlfunc/"open">.
5. Separate target-specific data
Use =begin FORMAT ... =end FORMAT when content is meant for one particular processor, such as HTML. Inside a region whose name does not start with a colon, non-command paragraphs are data and are not parsed for ordinary Pod formatting codes. A colon changes that: =begin :FORMAT keeps the contents subject to normal paragraph processing.
=begin html
<em>Only an HTML formatter should use this.</em>
=end html
Regions must nest properly. If you open =begin outer, then =begin inner, close =end inner before =end outer. Do not use a target-specific region as a way to hide a malformed command from the checker.
6. Verify the finished document
Add the list and link examples to
/tmp/widget.pod, then run the checker again.podchecker /tmp/widget.podAsk the installed formatter to produce HTML in a separate temporary directory.
pod2html --outfile=/tmp/widget.html --htmlroot=/tmp --htmldir=/tmp /tmp/widget.pod test -s /tmp/widget.html && echo "HTML created" rm -f /tmp/widget.html /tmp/widget.podThese commands touch only temporary files. The final
rmis safe here because the paths are explicit scratch outputs. If you want to inspect the HTML first, omit that final command and remove the two files later.
Common traps
A command is ignored or rejected: check its first character, spelling and whether it is actually a recognised command. A command inside a non-colon
=beginregion is still a command, even though ordinary paragraphs there are data.Code layout changes unexpectedly: check whether the paragraph starts with a space or tab. Verbatim whitespace is significant, though a processor may expand tabs.
A list renders oddly: check that every
=overhas a matching=back, that item styles do not switch halfway through, and that numbered items stay sequential.A link points somewhere surprising: make the page, section, text and URL explicit. Older section-only forms such as
L<"OPTIONS">are ambiguous and deprecated; preferL</"OPTIONS">.
Done means
- Block bounded cleanly: the document has a clearly bounded Pod block and a blank line between paragraphs.
- Commands correct: commands are unindented, code examples are intentionally verbatim, and lists close correctly.
- Formatting unambiguous: formatting codes and links use escaped or multiple-angle syntax where needed.
- Regions isolated: target-specific data sits in correctly nested
=beginand=endregions. - Checks pass:
podchecker /tmp/widget.podpasses, andpod2htmlcreates non-empty output.