Service A needs something from service B. When should that be a synchronous call and when should it be an event?
A synchronous call couples availability and latency but keeps the outcome knowable at the point of decision; an event decouples both but converts every failure into something the caller will never learn about. Choose by asking whether the caller needs the answer to proceed, not by which style is fashionable.
What the interviewer is scoring
- Does the candidate distinguish temporal coupling from the domain question of whether the answer is needed now
- Whether they can state what happens to an event whose consumer is broken, and who notices
- That availability multiplies across a synchronous chain and they can do the arithmetic
- Whether they raise the ordering and idempotency obligations an event takes on
- Does the candidate name a case where asynchronous is the wrong answer rather than defending it universally
Answer
The question underneath the question
The useful test is not "which is more scalable". It is whether the caller can make its next decision without the answer. If a checkout cannot decide whether to show a confirmation page until the payment authorises, that is a synchronous dependency and dressing it up as an event only moves the waiting somewhere less visible. If a checkout wants a receipt emailed, the caller does not need to know the outcome to proceed, and holding the request open while an SMTP server is slow is a self-inflicted wound.
Most integration arguments go wrong because both sides are arguing about the mechanism when the disagreement is really about that question. Answer it first and the mechanism usually follows.
What a synchronous call actually couples
Three things, and they are worth separating because teams often accept all three when they only meant to accept one.
It couples availability: B being down means A is down for that operation. It couples latency: A's p99 now contains B's p99, and tail latencies compose badly. It couples capacity: a traffic spike on A becomes a traffic spike on B whether or not B was provisioned for it.
The availability arithmetic is what candidates most often skip. Independent dependencies multiply, so a chain of four services each at 99.9% gives roughly 99.6% for the composite — from about 43 minutes of monthly downtime to nearly three hours. That is a real result and it is why a deep synchronous call graph degrades faster than any single service in it. The mitigation is not to make each service better; it is to have fewer of them on the critical path.
What an event couples instead
An event removes temporal coupling and replaces it with a set of obligations that are easy to underestimate.
The consumer must tolerate duplicates, because every practical broker delivers at least once and a redelivery after a partial failure is normal operation rather than an edge case. It must tolerate reordering, unless you have partitioned by a key that guarantees order for the entities that need it, which constrains your partitioning strategy for the life of the topic. And someone must own the poison message: an event that fails every attempt, lands in a dead-letter queue, and sits there until a human notices.
That last one is the honest cost. In a synchronous design, a failure is returned to a caller who is still on the phone and can react. In an asynchronous design, a failure happens in a process nobody is watching, and the business consequence — an order that never shipped, a refund that never issued — surfaces days later through a customer complaint. Asynchronous integration does not remove failure. It converts loud, immediate failure into quiet, deferred failure, and the price of admission is the monitoring that makes it loud again.
Where the boundary usually lands
A pattern that holds up: queries synchronous, state changes asynchronous, and commands whichever the caller's decision requires. Reading a customer's credit limit to decide whether to accept an order is a query and wants an answer now. Telling six downstream systems that an order was accepted is a fact that has already happened, and each consumer's failure to process it should not retroactively fail the order.
The awkward middle is a command that changes state and whose outcome the caller needs. Payment authorisation is the canonical one. The usual resolution is to stay synchronous but bound the damage — a timeout shorter than the caller's own budget, a circuit breaker so a slow dependency stops consuming caller threads, and a defined behaviour on failure that is a product decision rather than a stack trace.
The failure candidates rarely mention
A synchronous call with a timeout has three outcomes, not two: success, failure, and unknown. When the timeout fires you do not know whether B processed the request. If A retries, and the operation is not idempotent, you have double-charged someone.
This is why an idempotency key on the write is not an optional refinement — it is what makes the retry safe, and without it the timeout you added for resilience has quietly created a correctness bug. Candidates who introduce timeouts and retries without addressing the unknown outcome have made the system less reliable while believing they made it more so.
Asynchronous does not mean the failure went away. It means nobody is standing there when it happens, so you have to build the thing that notices.
Likely follow-ups
- Your synchronous chain is four services deep, each at three nines. What is the end-to-end availability?
- The consumer of your event has been down for two hours. What does recovery look like?
- How would you let a caller stay synchronous while the work behind it is asynchronous?
- Where does a request-reply-over-a-queue pattern sit in this framing, and what does it actually buy?
Related questions
- The same domain has to be exposed to a mobile app, a partner integration and internal service-to-service traffic. Where does GraphQL fit, where does gRPC, and where does neither?hardAlso on api-design7 min
- You need to add one field to an event and remove another, and five teams consume it. How does that roll out?hardAlso on event-driven6 min
- When would you use the observer pattern, and what goes wrong with it at scale?mediumAlso on coupling4 min
- Clients are asking for page 4,000 of your /orders collection. How is that endpoint paginated, and what would you change?mediumAlso on api-design4 min
- Give me a real single-responsibility violation you have seen, and how would you refactor it?mediumAlso on coupling5 min
- Adapter, decorator and facade - what concrete problem does each one solve, and where would reaching for it be over-engineering?mediumAlso on coupling6 min
- The business says the system must be highly available. How do you turn that into a number, and what does that number cost?mediumAlso on availability4 min
- Design the account-information API a third party will call on behalf of your customers. Where does consent live, and what does it constrain?hardAlso on api-design6 min