DEV Community

Shingo Kawamura
Shingo Kawamura

Posted on

Why I Built a jq-Compatible Tool in Pure Perl (and Why It Still Matters)

Modern tooling often assumes a perfect world:
latest Linux, internet access, package managers, and rebuildable binaries.

Reality is different.

In many production environments — especially enterprise, legacy, or air-gapped systems —
those assumptions break down.

That gap is why I built jq-lite.


The Problem: jq Is Great — Until You Can’t Use It

jq is an excellent JSON processor.
But in real operations, I kept hitting walls:

Legacy UNIX systems with outdated glibc

Air-gapped environments with no package repositories

Minimal containers without build tools

Environments where “just install jq” was not an option

In those cases, JSON processing didn’t disappear —
the need became more critical.

Shell scripts still had to be reliable.
Automation still had to work.


The Idea: A jq-Compatible Tool That Never Breaks Scripts

I didn’t want another JSON tool.

I wanted a tool that could be:

Dropped into restricted systems

Trusted in shell scripts

Stable across years, not releases

So I built jq-lite with three strict rules:


Design Rule #1: Pure Perl, No Dependencies

jq-lite is written in pure Perl.

No native extensions

No external libraries

No compilation step

If Perl exists, jq-lite runs.

That means it works on:

legacy Linux / UNIX

minimal containers

offline environments

systems where only base OS tools are allowed

Perl is still one of the most widely deployed runtimes in the world —
especially in places modern tooling forgot.


Design Rule #2: jq Compatibility Where It Matters

jq-lite aims to be jq-compatible in daily CLI usage:

echo '{}' | jq-lite --arg greeting hello '.hello = $greeting'

Output:

{
"hello": "hello"
}

The goal isn’t to clone every feature,
but to support the jq patterns people actually use in automation.


Design Rule #3: A Stable CLI Contract (This Is the Important Part)

Here’s the part that made jq-lite different.

jq-lite defines a documented CLI contract:

exit codes

error categories

stderr prefixes

stdout behavior on failure

And that contract is tested.

Exit Code Meaning

0 Success
1 -e used and result was false / null / empty
2 Compile error
3 Runtime error
4 Input error
5 Usage error

This means shell scripts can safely rely on jq-lite:

if jq-lite -e '.enabled' config.json; then
deploy
fi

No surprises.
No silent behavior changes.

In operations, this matters more than features.


Why Not Rewrite It in Rust or Go?

I get this question a lot.

Because the problem wasn’t performance or language fashion.

The problem was deployability and longevity.

Perl already exists on:

old servers

enterprise systems

restricted environments

Adding jq-lite doesn’t require changing the environment —
just using what’s already there.


Who Is jq-lite For?

jq-lite is not for everyone.

It is for people who:

maintain legacy systems

write shell scripts that must survive years

operate in restricted or offline environments

care about CLI behavior as a contract, not a suggestion

If you’ve ever thought
“this script must not break in five years”
— jq-lite is for you.


Where to Find It

GitHub: https://github.com/kawamurashingo/JQ-Lite

CLI Contract: https://github.com/kawamurashingo/JQ-Lite/blob/main/docs/cli-contract.md

CPAN: https://metacpan.org/release/JQ-Lite

Alpine Linux package available


Final Thought

Modern tools optimize for speed of change.

jq-lite optimizes for stability over time.

Sometimes, that’s the more important optimization.


Author
川村慎吾(Shingo Kawamura)
SRE / Infrastructure Engineer
CPAN author of jq-lite

Top comments (1)

Collapse
 
pannakoota profile image
Shingo Kawamura

Thanks for reading!
jq-lite focuses on long-term CLI stability in legacy and constrained environments.
Feedback and real-world use cases are very welcome.