Preflight: catching bugs before runtime#
This chapter covers the pragmas, compile-time checks, and runtime hygiene settings that prevent most bugs from reaching the debugger at all. A clean preflight is the shortest path to a short debugging session.
Readers who already use v5.36 or later run under strict and
warnings by default and can skip straight to the diagnostics and
autoflush sections. The rest of this chapter assumes you might be
maintaining older code where neither is implicit.
Strict and warnings#
Since Perl 5.36, use v5.36; (and any declaration of a higher
version) implicitly enables use strict; and use warnings;.
Modern scripts do not write those lines — the version declaration
covers both:
use v5.40;
use strict refuses three classes of sloppy code at compile time:
undeclared variables (strict vars), symbolic references
(strict refs), and barewords used as strings (strict subs).
use warnings turns on runtime diagnostics for uninitialised
reads, lossy numeric conversions, filehandles used after close,
and several dozen other sharp edges.
Legacy code that uses neither still benefits from both: add them, fix the symbols they complain about, commit.
Narrow opt-outs remain useful:
no strict 'refs'; # genuine symbolic deref
no warnings 'uninitialized'; # known-safe defaulting
no warnings 'experimental::try'; # on 5.36, 5.38 — see below
Never blanket-disable: no warnings; with no category list is
almost always wrong.
Promote warnings to fatal for a region:
local $SIG{__WARN__} = sub { die $_[0] };
Useful at program top during development; the first warning becomes a stack-trace-producing crash.
use diagnostics#
Expands each warning or fatal message into the full paragraph from
perldiag:
use diagnostics;
The output is verbose — too verbose for production — but during debugging it explains what a terse message like “Odd number of elements in hash assignment” actually means and how to fix it.
Post-hoc version: pipe a program’s stderr through the splain
command to annotate warnings after the fact.
Compile-time checks#
perl -c script.pl parses the script and runs every BEGIN /
use, then stops before the first runtime statement. A clean -c
means the file and its dependencies compile; it does not mean the
program runs correctly.
perl -c script.pl
script.pl syntax OK
Combine with -w to enable warnings globally for the compile
phase and any compile-time code:
perl -cw script.pl
perl -MO=Deparse script.pl prints what perl actually parsed —
precedence misparses, constant-folded branches, for rewritten as
while. Use it to verify that perl saw what you meant:
perl -MO=Deparse script.pl
perl -MO=Deparse,-p forces full parenthesisation and makes
precedence explicit.
perl -MO=Concise script.pl prints a compact op-tree dump; mostly
for readers doing deep optimisation work.
autodie: make failure throw#
Built-ins that signal failure via false return values (open,
close, chdir, unlink) are easy to forget to check. autodie
replaces them with versions that throw:
use autodie qw(open close chdir);
open my $fh, '<', $path; # now dies on failure; no `or die` needed
autodie is lexical: only calls in its scope are affected.
Narrow the opt-out for a specific call site:
{
no autodie 'open';
open my $fh, '<', $path or warn "cannot read $path: $!";
}
Prefer autodie to the retired Fatal module, which did not
cover functions returning counts.
Unbuffered output#
When diagnostic output vanishes before a crash, or arrives
interleaved oddly with warn messages, the cause is almost
always block-buffered stdout. Set $| at the top of the script:
$| = 1;
Every print now flushes
immediately. The cost is a small I/O-throughput hit; the gain is
that what you see on the terminal matches what the program has
actually done, in order.
For a specific filehandle without changing the selected one:
use IO::Handle;
$log_fh->autoflush(1);
Common early diagnostics and what they mean#
Diagnostic |
Meaning |
|---|---|
|
Strict is on and |
|
A list assigned to a hash has odd count; a |
|
An |
|
A package variable was referenced exactly once. Almost always a typo or a missing |
|
A |
|
|
Find out more#
print-and-die — when preflight is not enough and you need structured runtime diagnostics.
exceptions — turning failure paths into typed exceptions the caller can match on.