Every box in my systems has a why behind it. Each entry names the constraint, the options rejected, and the cost accepted.
A multi-agent platform behind a live commerce system: agent steps had to stay ordered per conversation, model calls could be slow, and any failed action needed retries that never blocked the user-facing flow.
— Synchronous calls between services — any slow model call becomes a user-facing timeout across the whole flow.
— One broker for both jobs — Kafka's retry story and RabbitMQ's ordering story are each the weak half of the other tool.
In a synchronous chain, the user's request waits on the model — slow model, frozen user. With pub/sub, the request is acknowledged instantly, the heavy work runs in the background, and the result returns through the notify step. Kafka carries the ordered event streams — the timeline of what happened, in order, replayable. RabbitMQ carries work distribution — retries, backoff, and dead-letter queues for actions that fail. Nothing blocks; the legacy platform never learned the agents existed.
Tenants 100x apart in data size sharing one platform, with a product promise of no noisy neighbors — each tenant should feel like the only customer.
— tenant_id columns in shared tables — isolation becomes query discipline instead of a physical guarantee, and every engineer is one WHERE clause away from a leak.
— One shared cache and one shared deploy — warmth and risk both follow the loudest tenant.
One proxy owns tenancy and routes each request to that tenant's own database. Hard isolation, per-tenant backups and tuning, cache warmth that belongs to the tenant, and blue-green deploys nobody notices. Isolation, resources, cache warmth and deploys are one problem — this solves it once, at the routing layer. And audits become demonstrations instead of promises: each tenant's data provably lives in its own database.