When a product is in early stage, team is small, sticking to a single language keeps the team in flow: builds are quick, hiring is simple, and nobody wrestles with two toolchains before coffee. Over time, a second language often slips in—maybe for an analytics job or a machine‑learning prototype. The trick isn’t adding that language; it’s how many people end up living in both worlds and paying the “overlap tax” in context‑switching.

Picture five engineers who speak TypeScript and five who speak Python. If only one person spends part of their week shuttling data across a thin API, everyone else stays in deep focus. But when half of each group are debugging build scripts in unfamiliar runtimes, what used to take minutes becomes a morning ritual of environment wrangling.
Let’s take an example of such a tradeoff in your messaging layer; choosing between Apache Pulsar and NATS JetStream.
Apache Pulsar is a powerhouse: tiered storage, geo‑replication, multi‑tenant isolation, and peak consumer throughputs measured in millions of messages per second—about twenty times what NATS JetStream can handle in public benchmarks (2.6 M msg/s vs 160 K msg/s)[ref]. Its rich feature set comes at the cost of extra services (ZooKeeper or Pulsar’s built‑in consensus plus BookKeeper) and native dependencies in its Node client.
NATS JetStream, by contrast, lives in a single 10 MB binary that you enable on your NATS server. You get persistence, replay, and simple stream abstractions without adding new runtimes. It delivers sub‑millisecond hops in small clusters and scales horizontally by adding nodes [ref]. You trade off Pulsar’s long‑term retention and built‑in isolation, but you avoid extra binaries, CI gymnastics, and a second language at runtime.
Especially for small teams in early stage startups, I tend to lean toward the option that keeps overlap as low as possible: let the TypeScript folks stay in JetStream and the Python folks keep their backend pure, with a single, clear interface in between. One neat boundary beats a blurry Venn diagram every day.
A skeptic might point out that strict language silos can leave performance and reliability on the table. Polyglot teams can pick the best tool for each layer—Pulsar for high‑scale streaming, Rust for data crunching, Python for ML—rather than the tool that just matches the app stack. Modern containerized environments, monorepo tooling, and remote build caches can shrink context‑switching costs, and engineers comfortable in multiple languages can unlock Pulsar’s full power without hand‑tying architecture to a single stack.
Ultimately, the right choice depends on where your product is in its lifecycle and on the size and skill mix of your team. Early on, simplicity wins; at scale, raw features may justify a polyglot approach.
Leave a comment