# 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: ```perl 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: ```perl 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: ```perl 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`: ```perl 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: ```perl 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: ```perl { 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: ```perl $| = 1; ``` Every [`print`](../../p5/core/perlfunc/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: ```perl use IO::Handle; $log_fh->autoflush(1); ``` ## Common early diagnostics and what they mean | Diagnostic | Meaning | |---|---| | `Global symbol "$x" requires explicit package name` | Strict is on and `$x` was never declared; add [`my`](../../p5/core/perlfunc/my). | | `Odd number of elements in hash assignment` | A list assigned to a hash has odd count; a `qw(...)` or string with an embedded space produced two values where one was expected. | | `Use of uninitialized value in ...` | An [`undef`](../../p5/core/perlfunc/undef) reached an operator. Usually a typo in a variable name or a missing return. | | `Name "main::x" used only once: possible typo` | A package variable was referenced exactly once. Almost always a typo or a missing `my`. | | `Can't locate Foo.pm in @INC` | A [`use`](../../p5/core/perlfunc/use) or [`require`](../../p5/core/perlfunc/require) could not find the module. Check spelling, `@INC`, `PERL5LIB`. | | `Reference found where even-sized list expected` | `my %h = { ... }` — braces build a hashref; use parens. | ## Find out more - [print-and-die](print-and-die) — when preflight is not enough and you need structured runtime diagnostics. - [exceptions](exceptions) — turning failure paths into typed exceptions the caller can match on.