All posts

Temporal built one Rust core for 8 language SDKs

Manaal KhanJune 28, 2026 at 3:32 AM5 min read
Temporal built one Rust core for 8 language SDKs

Key Takeaways

Temporal built one Rust core for 8 language SDKs
Source: InfoQ
  • Temporal's SDK team wrote 70,000 lines of Rust once instead of replicating complex state machine logic in 8 languages
  • FFI boundaries, async bridging, and memory safety are the key technical challenges when exposing Rust to other languages
  • WebAssembly is emerging as a cleaner alternative to native extensions for cross-language SDK architecture

Writing the same business logic seven times in seven languages is a bad idea. Spencer Judge, who leads the SDK team at Temporal, has a slide to prove it: a state machine diagram backed by 800 lines of code, part of a 70,000-line repository that must live in every SDK Temporal ships. His solution? Write it once in Rust, then build thin language-specific layers on top.

Judge presented this architectural pattern at QCon San Francisco, walking through the practical decisions that shaped Temporal's polyglot SDK strategy. The talk offers a blueprint for any engineering team facing the same multiplication problem.

Why Temporal couldn't avoid fat SDKs

Temporal is a durable execution platform. Users write workflows in their language of choice, and Temporal makes those workflows resilient. The catch: durability guarantees require complex client-side logic. Making an RPC call for every tiny decision wouldn't scale, so the SDK has to be thick.

That thickness is the problem. Temporal supports Go, Java, Python, TypeScript, .NET, PHP, Ruby, and more. Maintaining identical behavior across all of them, with separate implementations, would mean duplicating thousands of lines of subtle state machine code. Bugs would diverge. Features would lag. Testing would multiply.

Judge showed the audience a diagram of just one component, a "local activity," with its 800 lines of backing code. Then he zoomed out: the full state machine layer is about 7,000 lines, sitting inside a 70,000-line repository. "Who would want to write this seven times?" he asked. No hands went up.

Advertisement

The Rust core pattern

Temporal's answer is a shared core written in Rust. All the complicated durability logic lives there. Each language SDK wraps that core through a foreign function interface (FFI), exposing idiomatic APIs to users without reimplementing the hard parts.

Rust fits this job well. Its ownership model provides memory safety without a garbage collector, which matters when your code crosses into runtimes managed by Python's GIL or Java's JVM. Rust compiles to native code, so performance stays predictable. And the language's type system catches errors at compile time that would otherwise surface as subtle bugs in production.

The pattern isn't unique to Temporal. Discord rewrote parts of their Python stack in Rust for similar reasons. Signal's encryption library uses a Rust core with bindings for iOS, Android, and JavaScript. The approach scales wherever complex logic must behave identically across language boundaries.

The hard parts: FFI, async, and memory

Judge didn't pretend this is easy. Three challenges dominate:

  • FFI boundaries: Crossing from Rust into Python or TypeScript means flattening Rust's rich types into C-compatible primitives, then rebuilding them on the other side. Every boundary is a place for bugs to hide.
  • Async bridging: Rust's async model doesn't map cleanly to Python's asyncio or JavaScript's event loop. The SDK team had to translate between concurrency models without introducing deadlocks or race conditions.
  • Memory safety: Rust doesn't use a garbage collector. When the core hands memory to a language with GC, ownership rules get murky. Leaks and double-frees lurk in poorly designed handoffs.

Native extensions, the traditional solution for embedding compiled code in interpreted languages, add their own pain. They're platform-specific, require separate build pipelines, and often break when the host language upgrades. Temporal ships pre-built binaries, but supporting every OS and architecture is a maintenance burden.

Advertisement

WebAssembly as the next step

Judge flagged WebAssembly as a potential simplification. Instead of compiling Rust to native code for each platform, you compile once to Wasm and run it inside any host that supports the WebAssembly runtime. Node.js, browsers, and even some server-side runtimes can execute Wasm with near-native speed.

The appeal is clear: fewer build targets, simpler distribution, and a sandboxed execution model that sidesteps some memory safety concerns. The tradeoff is maturity. Wasm's interface types proposal is still evolving, and performance in heavy async workloads lags native code. But for teams starting fresh, it's worth evaluating.

When this pattern makes sense

Not every SDK needs a Rust core. If your logic is simple, if you only support two languages, or if your team lacks Rust experience, the upfront cost may not pay off. But the calculus shifts when:

  1. The core logic is complex enough that maintaining parity across languages is painful.
  2. You're targeting four or more languages.
  3. Correctness matters more than velocity. Financial systems, encryption, and workflow orchestration fall here.
  4. Your team can invest in the FFI plumbing once and amortize it over years.

Temporal fits all four criteria. Most teams won't, but the ones that do have a proven model to follow.

ℹ️

Logicity's Take

The Rust-core pattern is a forcing function for API design discipline. Because every public function must cross an FFI boundary, you can't cheat with language-specific hacks. That constraint produces cleaner interfaces. Teams considering this approach should evaluate alternatives too. Mozilla's UniFFI automates much of the binding generation. Nickel and Cap'n Proto handle cross-language serialization. For Wasm-first builds, Extism and Wasmer offer higher-level abstractions. Temporal's stack is custom-built, but the ecosystem is catching up.

Frequently Asked Questions

What is a polyglot SDK?

A polyglot SDK provides the same functionality across multiple programming languages. Users interact with idiomatic APIs in their language while the underlying logic is shared, often through a compiled core or server-side service.

Why use Rust instead of C for a shared SDK core?

Rust offers memory safety guarantees that C lacks, reducing the risk of crashes and security vulnerabilities when code crosses language boundaries. Its type system also catches errors at compile time.

How does Temporal's Rust core communicate with Python or Java?

Through foreign function interfaces. The Rust code exposes C-compatible symbols that each language's runtime can call. Data crosses the boundary as primitive types or serialized bytes.

Can WebAssembly replace native FFI for cross-language SDKs?

Partially. Wasm simplifies distribution and sandboxes execution, but its interface types are still maturing. Performance in async-heavy workloads can lag native code.

What are alternatives to building a custom Rust core?

Mozilla's UniFFI generates bindings automatically. gRPC or similar RPCs move logic to a sidecar service. For simpler cases, code generation from a shared specification works.

ℹ️

Need Help Implementing This?

Planning a polyglot SDK or evaluating Rust for your core infrastructure? Logicity connects engineering teams with consultants who specialize in cross-language architecture and FFI design. Get in touch to discuss your stack.

Source: InfoQ

Advertisement
M

Manaal Khan

Tech & Innovation Writer

Produced with AI assistance and reviewed by the Logicity editorial team. Learn more in our Editorial Policy.

Related Articles