Perl documentation (including POD) with Markdown and Docbook
Introduction
I have uploaded to CPAN some tooling modules which allow Perl documentation to be authored as Markdown files - or Docbook XML articles - then merged into source files as POD. They can also then be published as HTML to static sites such as Github pages or Cloudflare Workers.
TL;DR here is an example of the end result - documention for the Cloudflare::API module created from the doc/ directory in the GitHub repository and POD for the command-line tool created from a Markdown file in the same repository.
Perl Documentation
Documentation can be tedious - despite knowing how important good documentation is, and that it can make or break open-source software. Nothing is worse than trying to run "man Some::Module" (or "perldoc Some::Manual") and getting nothing back.
POD is the "linga franca" of the Perl world for a good reason - it has stood the test of time, is completely integrated into the Perl ecosystem and is an excellent format for creating manual pages for Perl modules and scripts.
However arguably it doesn't translate well into the modern world of GitHub and other revision control systems, where Markdown is natively rendered by front-end browsers - and it's difficult for users to parse POD from the source code of your Perl file if they just want to understand what it does.
Markdown as POD
I have been using Markdown (and Docbook - more on that later) for documentation in Perl projects for a while now, and have released some tooling CPAN modules that allow Markdown sidecar files to be merged into their source equivalent as POD.
What is a Markdown sidecar file ? It's simply the same name as the source file with a .md extension appended.
Here is what a typical Perl distribution source repository may look like when using Markdown sidecar documentation:
Example-Client/
├── Makefile.PL
├── doc/
│ ├── example-client.xml
│ ├── example-client.md
│ └── images/
│ └── request-flow.svg
├── lib/
│ └── Example/
│ ├── Client.pm
│ └── Client.pm.md
└── bin/
├── example-client
└── example-client.md
Ignoring the doc/ directory for the moment (more on that later) we can see that in the lib/ and bin/ directories each file has an associated sidecar companion file with a .md extension.
Using the CPAN Markdown::Pod::Embed module we can:
# Install the module
cpanm Markdown::Pod::Embed
# Makes the markpod command available
markpod --inplace --nobackup --recursive lib bin
And all Markdown in the companion sidecar files will be converted to POD and appended to their source files.
WARNING: Any existing POD in the source files will be replaced !
Makefile targets for POD merge
Running the markpod command is a bit cumbersome. If you install the supporting CPAN MakeMaker extension module ASPEER::MakeMaker::Markdown::Pod can run make doc as a Makefile target:
# Install the module
cpanm ASPEER::MakeMaker::Markdown::Pod
# Build Makefile with Markdown::Pod::Embed extension
perl -MASPEER::MakeMaker::Markdown::Pod Makefile.PL
# Make the documentation
make doc
This will convert all module and script sidecar files to POD and embed them into their original source files.
Why use the author namespace ? In short I did want to trample over the top level MakeMaker namespace, nor did I want to claim the ExtUtils::Markdown::Pod namespace.
As a bonus if "make doc" detects a README.md file in the distribution root it will convert it (assuming pandoc is installed) to a plain text README file.
What about other documentation ?
Sometimes a README.md or "perldoc Module::Name" isn't enough. You've got a raft of information you want to convey about how your module works, examples, screenshots, code demos etc.
Simple enough. Put it in the doc/ folder as Markdown and it will be available to anyone browsing the Github repository in a format which is reasonably viewable.
Usually that is fairly good, but it would be nice to publish it to Github pages - or another static site - in a simple way. This is where the CPAN module Markdown::Publish comes it. It will convert Markdown files in doc/ to static HTML via publishing engines such as Mkdocs Material, Docusaurus, Vitepress and Astro Starlight and push them to either Github Pages or (if you have an account) Cloudflare Worker Pages.
The Markdown::Publish module will split your Markdown into pages/sections at the heading boundary and sluggify them for navigation.
Assuming you have the directory layout above (and all system prerequisites such as Mkdocs Material installed):
# Install the module
cpanm Markdown::Publish
# Build the site
markdown-publish build
# Preview locally
markdown-publish serve
# Push to Github pages
markdown-publish gh-push
Makefile targets for Publishing
In the same way there is a module to create Makefile targets for the POD merge, there is one for Markdown publishing:
# Install the module
cpanm ASPEER::MakeMaker::Markdown::Publish
# Build Makefile with Markdown::Publish extension
perl -MASPEER::MakeMaker::Markdown::Publish Makefile.PL
# Make the documentation and push to Github pages
make publish_gh-push
Docbook
Sometimes Markdown isn't sufficient for documentation, or using it for large documents is unwieldy. Docbook (specifically a Docbook Article) can be a good format for technical writing, and provides a wider semantic context for material in the article vs Markdown.
Docbook can be eaiser to edit with tools like the XXE XMLMind Visual Docbook editor (sadly going out of support in 2027 but still available free for non-commercial use).
The CPAN Docbook::Convert module can convert Docbook articles to Markdown, which is then published through the standard toolchain.
Assuming the Docbook::Convert module is installed (along with required system binaries such as pandoc ) the MakeMaker::Markdown::Pod helper module will convert any Docbook articles to Markdown, allow publishing.
Examples
You can see and example of the Markdown::Publish module using several of these techniques itself. The Markdown::Publish Github repository contains a doc/ directory with a Docbook XML file which has been converted to Markdown. That Markdown is then published to the modules Github pages location as documentation.
You can also see the Markdown sidecar files in the bin/ directory and the embedded POD in the markdown-publish script (at the bottom of the file)
These modules are also used to publish the WebDyne documentation from WebDyne GitHub repository and the Cloudflare::API documentation from the Cloudflare::API GitHub repository.
AI Disclaimer
Many of these modules predate AI and are human coded. The Docbook::Convert and Markdown::Pod::Embed are largely human but have AI bugfixes incorporated. The ASPEER::MakeMaker family of modules are almost all human coded. The Markdown::Publish module was mostly human coded for MkDocs publishing, but then AI was tasked with with adding support for other publishing engines and decoupling from MkDocs specifically.
I blog about Perl.
Leave a comment