Perl 5.30 Upgrade Checks for Regexes, I/O and JSON
You will finish with a short compatibility review for code moving to Perl 5.30 or later. It covers the changes most likely to turn an old warning into a failure: variable-length lookbehind, UTF-8 handle I/O, removed APIs, and JSON::PP's changed default. The examples are safe probes and do not edit your project.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about twenty minutes for the checks, plus time to run your own test suite. You need a shell, Perl and a copy of the application or module you are upgrading. The installed reference here is perl-doc 5.38.2-3.2ubuntu0.6; its perl5300delta page describes the differences between Perl 5.28.0 and 5.30.0. Later Perls can retain the change, add warnings or change diagnostics, so use your target interpreter for the final run.
1. Record the interpreter you will test
Start with an ordinary, read-only version check. Do not assume that the perl in your shell is the interpreter used by a service, virtual environment or build job:
$ perl -v
This is perl 5, version 38, subversion 2 (v5.38.2) built for x86_64-linux-gnu-thread-multi
Repeat the check with the exact path or wrapper used by the application. A passing probe on Perl 5.38 does not prove that a Perl 5.30 deployment has the same patch level, module set or locale configuration.
Checkpoint: save the version and the command used to run the test suite in the upgrade notes. This prevents a later shell or CI job from silently testing a different interpreter.
2. Find code that depended on rejected syntax
Perl 5.30 made a declaration such as my $value if 0 fatal. It had been deprecated since earlier releases, so search the source before upgrading:
$ rg -n 'my\s+\$[A-Za-z_][A-Za-z0-9_]*\s+if\s+0' lib bin t
The expression is only a search hint. Review each match rather than applying a blind replacement, because a declaration's intended scope matters. Usually the fix is to declare the variable before the conditional and assign it inside the branch.
You can confirm the failure mode without changing a file:
$ perl -e 'my $value if 0'
This use of my() in false conditional is no longer allowed at -e line 1.
Checkpoint: run your normal compile-only pass after each source change. For a script, perl -c path/to/script.pl checks syntax without running its main code. It does not replace tests for module loading or runtime behaviour.
3. Review regular expressions that use new lookbehind
Perl 5.30 experimentally supports limited variable-length lookbehind. The maximum lookbehind length is 255 characters, and the feature raises a warning in the experimental::vlb category. That warning is a compatibility signal, not permission to assume the feature is stable across every Perl release.
$ perl -we '"abc" =~ /(?<=foo?)a/'
Use of experimental variable length lookbehind is experimental at -e line 1.
Search for lookbehind assertions and test their boundaries, including the shortest and longest permitted paths:
$ rg -n '\(\?<[!=]' --glob '*.pl' --glob '*.pm' .
Keep the warning visible while you test. If the expression is not essential, a fixed-width assertion or a two-stage match is often easier to support. If it is essential, document the required Perl version and assert the expected matches in tests. Do not suppress all experimental warnings just to make an upgrade green.
4. Fix raw I/O on UTF-8 handles
Perl 5.30 made sysread, syswrite, send and recv fatal when used on a handle with the :utf8 layer. These operations work on bytes, while the layer describes decoded text. Mixing the two can produce invalid data.
$ perl -e 'open my $fh, "<:utf8", "/etc/hostname" or die $!; sysread($fh, my $buf, 1)'
sysread() isn't allowed on :utf8 handles at -e line 1.
Search for both the raw calls and the layer setup:
$ rg -n 'sys(read|write)|\b(send|recv)\b|:utf8|:encoding' lib bin t
Choose the interface that matches the data. Use read or sysread on a binary handle when you need bytes, or use ordinary text reads on a handle with the appropriate :encoding(...) layer when you need characters. Do not fix the error by removing an encoding layer until you have checked the protocol and its tests.
5. Check removed APIs and JSON input assumptions
File::Glob::glob() was removed from the core distribution's usable API and now advises File::Glob::bsd_glob(). Search for the fully qualified call, then test the replacement against filenames containing spaces, brackets and hidden files as appropriate for your application:
$ rg -n 'File::Glob::glob\s*\(' .
$ perl -MFile::Glob=bsd_glob -e 'print "@{[bsd_glob("*.pm")]}\n"'
Do not infer that every glob call needs this change. The issue is the removed File::Glob::glob() function, not the shell's pathname expansion or every use of a different glob interface.
JSON::PP also changed: allow_nonref is enabled by default, so encoding a scalar such as 42 is accepted:
$ perl -MJSON::PP -e 'print encode_json(42), "\n"'
42
That is a library default, not a guarantee that a receiving API accepts a scalar JSON document. If your protocol requires an object or array at the top level, validate that contract explicitly before encoding or after decoding.
6. Run the upgrade gate and keep a rollback
Run the project's compile checks, unit tests and integration tests with the target Perl. Pay particular attention to warnings promoted to failures, locale-sensitive regular expressions, binary protocols and code that shells out to glob or JSON helpers. Keep the old interpreter and dependency lockfile available until the target has passed in the same deployment environment.
These checks change no service, file or interpreter state, so there is no undo command. If an upgrade has already replaced a runtime, recovery means restoring the previously tested package or image through your normal deployment rollback, then fixing the failing code before retrying.
Done means
- The application and test suite were run with the intended Perl binary and version recorded.
- False-conditional declarations and removed
File::Glob::glob()calls have been reviewed. - Variable-length lookbehind is covered by tests and its experimental warning is understood.
- Raw byte I/O is separated from UTF-8 or encoded text handles.
- JSON top-level values are checked against the receiving protocol, not accepted accidentally because of a default.
- The previous tested runtime remains available until the upgrade passes its deployment checks.