Sitemap
Anudha Mittal

Anudha Mittal

4 min readJun 21, 2026

--

Interaction between languages

Do not model Python as “a subprocess per function.”
Model it as a long-lived language worker with an RPC boundary.

JavaScript app
|
| pyjs.call("analyze_har", args)
| pyjs.stream("crawl_site", args)
v
Python worker process / pool
|
| dispatches to registered Python functions
v
Python modules

Core design:

1. Start Python once
- one persistent Python worker
- or a small pool: python-worker-1, python-worker-2, etc.
2. JS calls Python by message
{
id: "req_123",
fn: "extract_har_features",
args: {...},
timeout_ms: 30000
}
3. Python returns structured events
{ id: "req_123", type: "progress", data: {...} }
{ id: "req_123", type: "chunk", data: {...} }
{ id: "req_123", type: "result", data: {...} }
{ id: "req_123", type: "error", error: "..." }
4. JS awaits final result but can also receive stream

So the API should feel like this:

const result = await pyjs.call("classify_page", {
url: "https://example.com"
}, {
timeoutMs: 30_000,
onProgress: (msg) => console.log(msg)
});

And streaming:

for await (const event of pyjs.stream("monitor_browser_memory", { pid })) {
console.log(event);
}

Python side:

@jspy.function("classify_page")
def classify_page(args, ctx):
ctx.progress({"stage": "loading"})
result = do_work(args["url"])
return result

The important pieces:

pyjs = JavaScript client module

Responsibilities:

- start/reuse Python worker
- send JSON or MessagePack requests
- assign request IDs
- await final result
- expose async iterator for streams
- enforce timeout
- cancel requests
- restart crashed worker

jspy = Python server module

Get Anudha Mittal’s stories in your inbox

Join Medium for free to get updates from this writer.

Responsibilities:

- register callable Python functions
- read messages from transport
- dispatch to correct function
- emit progress/chunk/result/error events
- handle cancellation
- send heartbeat

Transport options, from simplest to strongest:

stdio        simple, good for local tools
Unix socket better for long-running local service
WebSocket good if browser/extension talks to local daemon
gRPC better for typed production services
shared memory only for large binary arrays, not control flow

For your case, I’d start with:

JS app
-> persistent Python daemon
-> WebSocket or stdio
-> JSON messages first
-> later MessagePack + shared memory for large HAR/blob data

The design principle:

stdio is not the function interface.
stdio/WebSocket/socket is only the transport.
The actual interface is:
function name + typed args + request id + timeout + stream events + final result.

This is the missing abstraction. Chrome does not create a new process for every JS function because it has a runtime. Your design needs the same thing: a Python runtime kept alive behind a clean JS-facing function API.how to train and scale a model that can serve a web index?

how search, recommendations, and transformer models can converge. You care about your research being applied to product and systems that millions use.

— — ~ — — ~ — — ~

Today I want to discuss machine learning systems from the perspective of information flow rather than individual tools.

A common misconception is that machine learning begins with training and ends with deployment. In practice, most operational complexity comes from everything surrounding the model.

Consider a recommendation system.

User interactions generate events. Those events are collected, transformed, stored, used for training, used for evaluation, and eventually influence future recommendations.

This is one reason similar architectural patterns appear repeatedly across MLOps tooling. Airflow orchestrates workflows. Kubernetes orchestrates containers. MLflow organizes experiments. Kafka organizes event streams. The interfaces differ, but all of them are managing state transitions within a larger system.

For example, when a training pipeline fails, the immediate question is usually not whether the model architecture is correct. The first questions are often: Which dataset version was used? Was the feature computation reproducible? Did upstream data arrive on time? Did infrastructure constraints change training behavior?

The same pattern appears after deployment. Teams frequently focus on model accuracy, but operational metrics often become leading indicators. Changes in latency, feature freshness, queue depth, retry rates, or resource utilization may reveal problems before accuracy metrics move.

I also find it useful to compare machine learning systems with distributed systems. Both involve moving information through multiple stages while preserving consistency and observability. A feature store is partly a machine learning concept and partly a data systems concept. Model registries solve some of the same governance problems that artifact repositories solve in traditional software engineering.

One recurring theme is reproducibility. Airflow DAGs, container images, experiment tracking systems, infrastructure-as-code, and model registries all address the same underlying question: if something breaks today, can we reconstruct the exact conditions that produced yesterday’s result?

Feedback loops are another recurring theme. Production predictions generate user behavior. User behavior generates data. Data influences future models. Because the output eventually becomes part of the input, machine learning systems require monitoring beyond traditional software systems.

When I evaluate MLOps architecture, I usually spend less time thinking about individual products and more time identifying where state is created, transformed, persisted, and observed. The specific tools change over time, but those system boundaries tend to remain surprisingly consistent.

--

--