Skip to main content
If you like events in theory but hate the day-to-day developer experience, this page is for you. Common pain points:
  • calling boilerplate (emit + await completion + unwrap result) for every request
  • eventual consistency anxiety (“will my response event arrive?”)
  • duplicating signatures across schemas, handlers, and implementation functions
The goal here is to keep event architecture benefits without forcing painful calling patterns.

1) Pain: painful calling interface boilerplate

You usually end up writing verbose call sites repeatedly. events_suck.wrap(...) gives you a method-shaped client API (client.create(...)) while still routing through events.

Minimal end-to-end wrap(...) wiring

Related docs:

2) Pain: eventual consistency headaches

If your mental model is “I called something, I need a result now,” pure fire-and-forget event flows can feel stressful. Two patterns reduce that stress:
  • request/response on one bus with direct return values (event_result() after now({first_result: true}) / wait({first_result: true}))
  • immediate execution for nested calls inside handlers (RPC-style queue-jump)
These patterns feel function-like for in-process flows. If you later move a step across process/network boundaries (bridges), treat that edge as eventually consistent again. Immediate execution docs: Immediate Execution (RPC-style)

Nested request/response with immediate execution

Related docs:

3) Pain: defining signatures multiple times

You can keep one source of truth for payload shapes and reuse it in implementation code.

One source of truth examples

Use implementation function signatures as the source of truth, then generate event classes from them.
Related docs:

Migration playbook

  1. Start with wrap(...) to clean up call-site boilerplate first.
  2. Use immediate execution patterns where you need function-call-like request/response behavior.
  3. Consolidate types with @validate_call (Python) or z.infer (TypeScript) to avoid signature drift.
  4. Add timeouts/retry policies where needed, instead of forcing eventual-consistency semantics everywhere.
You do not need to choose between clean DX and events. You can keep method-shaped APIs and adopt event internals incrementally.