· 6 min read ·

The $500K Case for Porting JSONata Out of JavaScript

Source: simonwillison

JSONata is one of those libraries that enterprise developers know well and most others have barely encountered. Created at IBM and baked into Node-RED, App Connect, and a growing catalog of data integration tools, it gives you a concise expression language for querying and transforming JSON documents. Where SQL operates on tables and XPath operates on XML, JSONata operates on nested JSON with syntax that draws from both.

A basic JSONata expression looks like this:

Account.Order.Product.(Price * Quantity)

That traverses a JSON document, finds all matching products across all orders on the account, and computes the extended price for each. The full language covers filtering, sorting, grouping, lambdas, custom functions, and a standard library of over fifty built-ins covering strings, numbers, dates, and array manipulation. For anyone building data pipelines or API transformation layers, it solves real problems that simpler path languages cannot touch.

The reference implementation is JavaScript, maintained under the jsonata-js organization on GitHub. It runs in Node.js and in browsers. For many use cases, that is perfectly adequate. But as documented in this writeup covered by Simon Willison, at production scale the JavaScript implementation starts carrying real and measurable costs, and a team called Vine ported it using AI assistance in roughly a day, trimming $500K from their annual cloud bill.

Why JavaScript Hurts at Scale

The economics run through several overlapping cost vectors when JSONata is deployed in serverless environments like AWS Lambda.

The first is cold start latency. A Node.js Lambda function needs to initialize the V8 runtime, load your module graph, and JIT-warm the interpreter before the first invocation returns. Cold starts for Node.js functions typically run 200 to 400 milliseconds depending on package size and import depth. A compiled Go or Rust binary on the same infrastructure starts in under 5 milliseconds. For synchronous API transformations where client latency matters, this difference compounds directly into p99 response times.

The second is memory footprint. Node.js has a baseline memory cost of roughly 40 to 60 megabytes before any application code loads, and a nontrivial JSONata evaluation adds GC pressure on top of that baseline. Lambda bills per GB-second. If you drop a function’s memory allocation from 512MB to 128MB by switching to a compiled implementation with a tighter runtime, you cut that dimension of your bill by 75 percent for the same workload volume.

The third is execution time itself. V8 is a sophisticated JIT compiler, but JSONata evaluates an expression by walking an AST and dispatching through JavaScript function calls, generating intermediate JavaScript objects at each step. A native implementation can process that same AST with tighter dispatch, better cache line behavior, and no GC pressure from ephemeral object allocation. For short-lived transformation workloads, the difference in pure evaluation time can easily be an order of magnitude.

At sufficient throughput, these three factors compound into real money. If your service evaluates JSONata expressions on every API request and you are handling tens of millions of requests per day, the difference between a JavaScript runtime and a native compiled one is measurable in six figures annually. The $500K figure cited in the Vine post is striking but not implausible for a data integration service at that scale.

What Porting JSONata Actually Requires

JSONata is not a trivial library to port. The language specification covers:

  • A PEG-style recursive descent parser that handles operator precedence, lambda definitions, and partial function application
  • An evaluator that walks the AST and handles JSONata’s distinctive sequence semantics, where singleton values and arrays are unified in non-obvious ways
  • The $$ root context binding and $ current context binding, which interact with path navigation in ways that differ from naive JSON path evaluation
  • The ~> chain operator for functional composition
  • A full standard library with specific edge-case behavior documented in the test suite
  • Regular expression support with JSONata-specific evaluation rules

The JavaScript reference implementation spans several thousand lines. Porting it manually to a compiled language would be weeks of careful work: understanding the parser thoroughly, replicating subtle evaluation semantics, and grinding through the test suite until every edge case passes.

This is precisely the kind of task where AI coding assistance provides genuine acceleration. The code to be ported is concrete, well-scoped, and validated by an existing test suite. The semantics are defined by the tests rather than by ambiguous product requirements. The transformation required is mechanical: translate JavaScript idioms into the target language while preserving observable behavior. There is no design work, no requirements clarification, and no architectural ambiguity in the task.

Large language models handle this pattern well. Given a JavaScript function like the core evaluate dispatch loop, a model can translate it to Go or Rust with reasonable fidelity. The human’s job becomes reviewing the translation, running the test suite, and fixing the subset of cases where the model made a mistake, usually in the handling of edge cases that required understanding implicit JavaScript coercion behavior. A one-day timeline for a complete port implies high model accuracy on this particular task combined with a strong existing test suite to validate against.

The Broader JSON Query Language Landscape

JSONata occupies a specific niche that its alternatives do not fill.

JMESPath is simpler and has production-quality implementations in Go, Rust, Python, Java, and several other languages. The AWS CLI uses it for output filtering. But JMESPath has no arithmetic, no user-defined functions, and no aggregation, which makes it fast to implement and embed but insufficient for real transformation work.

jq has a rich expression language and handles complex transformations well, but it is primarily a command-line tool and its embedding story is fragmented. The libjq C library exists but is not well-suited to being called from arbitrary runtimes with low overhead.

JSONPath was formalized as RFC 9535 in 2024, finally standardizing the path syntax used informally in many tools. It covers path navigation and filtering but not transformation.

JSONata fills the gap between simple path queries and full-blown scripting: expressive enough for real transformation work, formally specified, and backed by a comprehensive test suite. The cost of that expressiveness is implementation complexity, which is why there are few production-quality implementations outside JavaScript. The existing Java port, jsonata4java, has historically lagged the reference implementation in spec coverage. A compiled Go or Rust port with full test coverage and active maintenance would be a meaningful addition to the ecosystem.

Where AI Coding Assistance Actually Works

The Vine story is a useful data point because it is unusually clean. Porting tasks have a property that most software development lacks: the output is fully constrained. You are not making design decisions; you are reproducing existing behavior in a new environment. The test suite defines done. The original code defines the target behavior. The only open question is translation quality.

This is structurally different from using AI to write new features, where hallucinated APIs, wrong assumptions about system behavior, and underspecified requirements create slow feedback loops. Porting a well-tested library is closer to compilation than to engineering. The model is translating, not designing.

That distinction matters for setting honest expectations about AI-assisted development. The teams reporting genuine productivity gains tend to be working on tasks with this shape: translation, migration, boilerplate generation, test expansion, format conversion. The teams reporting frustration tend to be applying the tools to tasks that require reasoning about implicit requirements and system-wide context.

A half-million-dollar annual saving from a single day of AI-assisted porting is a compelling number to put in front of any engineering organization still skeptical of these tools. More precisely, it is a reminder that the value depends heavily on problem selection. The right question is not whether AI coding assistance is useful in general; it is which subset of your engineering backlog looks like this: well-specified, well-tested, mechanical translation from one form to another. That backlog is larger than most teams realize, and it has been waiting for exactly this kind of tooling.

Was this interesting?