Why One User Write Turns Into a Graph of Side Effects in Modern Backends
April 12, 2026
A user clicks Place Order and thinks they did one thing. The system did fifteen.
That gap between perceived simplicity and actual behavior is where most distributed systems mistakes hide. The API contract says insert one row. The reality is that a successful commit kicks off a graph of derived work: the primary database update, a cache invalidation, a search index refresh, an analytics event, a notification, an audit log entry, a recommendation recompute, and a handful of downstream stream consumers that each do their own thing.
None of that is optional. It is what makes the product feel coherent. But it is also where the system gets fragile.
The naive approach is to do all of it inside the request handler. Write the row, then push to Redis, then call the search service, then publish to Kafka, then send the email. If any one of those fails, you are stuck. Do you roll back the commit? You cannot, the row is already there. Do you retry the email forever? Now the request blocks. Do you ignore the failure? Congratulations, your cache and your database now disagree.
The fix is to stop treating the side effects as part of the write. The write commits. Period. Everything else hangs off a durable record of that commit and runs asynchronously.
That is what patterns like the transactional outbox and CDC actually buy you. The outbox is a row inserted in the same transaction as the user write. A separate worker reads it and fans it out. CDC reads the database log directly and emits change events. Either way, the source of truth is the database, and downstream systems subscribe to its changes instead of being called inline.
The concrete failure mode I have watched destroy a launch: a checkout flow that called the search index synchronously to update inventory counts. The search cluster did a leader election. Latency spiked to twelve seconds. Every checkout timed out. Orders that had already committed never sent confirmation emails because the email step came after the failing search call. Customers refreshed and placed the order twice. Support spent the week reconciling duplicates.
Decoupling the commit from the derived effects would have prevented every one of those problems. The order succeeds. The email arrives a few seconds later. The search index catches up when it is healthy.
Users see one write. Your system sees a graph. The job is to make sure the graph can heal itself when one node misbehaves.
A user writes once, but the system writes many times. The hard part is no longer storing the source of truth. It is keeping every derived system in sync around it.
Originally posted on LinkedIn. View original.