Two teams need the same customer data. Do you give them one shared database, or two copies that are allowed to disagree?
Name the single writer first, then choose which coupling you would rather own: a shared schema couples the two teams' release calendars, while a replicated copy couples them to a staleness window and obliges someone to detect and repair divergence. Both are permanent, so pick the one your organisation can pay for.
What the interviewer is scoring
- Does the candidate establish who writes the data before discussing how it is read
- Whether the trade-off is framed as choosing which coupling to own rather than as removing coupling
- That the cost of a shared schema is expressed as a constraint on both teams' release calendars
- Whether a replicated copy arrives with a stated staleness bound and a divergence-detection story
- Can they name what changes the answer as the two teams grow apart over two years
Answer
The question underneath the question
Before choosing a mechanism, establish which team owns the write. Almost every version of this problem that goes badly is one where two teams both write and neither is the authority, and no amount of clever replication rescues that; you have built a system with two truths and a race. So the first answer is that one side owns the data, the other reads it, and if both genuinely need to write then the boundary between the teams has been drawn in the wrong place and the interesting conversation is about the boundary rather than the database.
Once a single writer exists, the real choice appears, and it is not a choice about removing coupling. It is a choice about which coupling you would rather have. Sharing the store couples the two teams in time: their schema, their migrations and to some extent their release calendars become one thing. Replicating the data couples them in truth: the reader now works from a version of reality that is behind, and somebody has to own the gap. Candidates who present replication as decoupling have missed that a staleness window is a form of coupling with an operational cost attached.
What a shared database actually costs
The shared store is cheap on day one and its costs are all deferred, which is exactly why it wins arguments and then loses years. There is one copy, so there is nothing to reconcile, no lag, and no divergence to detect. Every consistency question is answered by the database you already run.
What you have given up is independent evolution. A column rename is now a cross-team change with a coordination cost, so it does not happen, so the schema accretes rather than improves. Worse, the reading team will start to depend on details the writing team never intended as contract — a nullable column that happens never to be null, a status value that happens to be ordered, an index that happens to make their query fast. The writing team then cannot change its own storage without breaking a consumer it cannot see, which is the precise moment a database stops being an implementation detail and becomes an unversioned public API with no documentation.
The failure mode to name is not corruption. It is that the reading team's slow report locks or saturates the store, and the writing team takes a production incident for a query they did not write, could not review, and cannot turn off. Read replicas and separate credentials with restricted grants make that survivable rather than solved, and they are worth stating because they are the cheap mitigation that most candidates skip.
What a replicated copy actually costs
Giving the reader its own copy, fed by change events or by change-data-capture from the writer's store, buys real things. The reader can shape the data for its own queries, index it as it likes, survive the writer being down, and be tuned without asking permission. The writer can refactor its schema freely as long as the published events hold. That is genuine independence and it is usually the right end state for two teams that will keep growing.
The bill arrives in three parts, and a strong answer prices all three. First, staleness: you must state the window you are promising, because "eventually" is not a design. A second is a different system from a minute, and a minute is a different system from an hour, and the reader's use case decides which of them is acceptable. Second, ordering and idempotency: events arrive out of order or twice, so the reader needs a version or sequence per entity and must apply updates so that replaying them changes nothing. Third, divergence: pipelines drop messages, mappers have defects, and a backfill run in the wrong order overwrites newer data with older. Divergence is not a possibility to be prevented, it is a certainty to be detected, so there has to be a periodic comparison that produces a number somebody looks at, and a documented way to rebuild the copy from scratch.
flowchart LR
W[Writing team service] --> S[(Owned store)]
S --> C[Change stream]
C --> P[Projection builder]
P --> R[(Reader copy)]
R --> Q[Reading team queries]
S --> X[Periodic comparison]
R --> X
X --> D[Divergence count with an owner]The edge worth pointing at is the one from the comparison to a named owner: without it the pipeline is a hope, and the first person to discover the drift will be a customer.
Choosing, and saying what would change your mind
The decision turns on a small number of facts, and being able to state them is what separates a judgement from a preference. How current does the reader need the data to be, expressed as a number rather than as "real time". How often does the writer need to change the schema, which is a proxy for how young the domain is. Whether the reader's queries are shaped like the writer's, because if they are, a copy buys little; if the reader wants aggregates over a year of history and the writer wants single-row lookups, one schema cannot serve both without one of them suffering. Whether the two teams can survive being in each other's release path, which is an organisational fact and not a technical one. And whether the data is subject to deletion obligations, because a second copy is a second place a deletion request has to reach.
A defensible default for two teams inside one product, with a young domain and no transactional need to read across the boundary, is a shared store with the reader on a replica and restricted grants, plus an explicit agreement that the writer's schema is not contract and a named view or set of views that is. That buys time and defers the pipeline until the coupling actually hurts. The trigger to move is observable rather than aesthetic: when schema changes start being deferred because of coordination cost, or when the reader's query patterns start dictating the writer's indexes, the shared store has stopped being cheap and you should pay for the copy.
The version that quietly becomes unmaintainable
The outcome to design against is neither of the two options. It is the hybrid nobody chose: a replicated copy that the reading team writes to as well, "just for their own fields". Now the copy cannot be rebuilt from the source without losing data, so the one thing that made replication survivable is gone, and every divergence investigation starts with an argument about which differences are legitimate. If the reader must own some fields, they belong in a separate store that the reader owns outright, joined at read time, so that the projection stays disposable.
The related failure is dual-write from the application layer into both stores, which looks like a small optimisation and is really a distributed transaction implemented by optimism. A crash between the two writes leaves permanent inconsistency with no mechanism that will ever notice it. Derive the copy from the source of truth, always in one direction, and keep the ability to throw it away.
Neither option removes coupling; one couples release calendars and the other couples you to a staleness window plus a reconciliation job. Say which coupling you are choosing, put a number on the staleness, and keep the replica disposable by never letting anyone write to it directly.
Likely follow-ups
- The reading team needs the data to be current to within a second for a billing calculation. Which options does that remove?
- How do you evolve a shared schema when one team needs a breaking change and the other has no capacity this quarter?
- Your replicated copy has drifted and nobody noticed for a month. How would you have found out sooner?
- When is the honest answer that these are one service and the team boundary is wrong?
Related questions
- An upstream team renames a field in the events you consume and nobody tells you. What should your pipeline have done?hardAlso on schema-evolution5 min
- How do you roll back a model in production?hardAlso on schema-evolution5 min
- How would you design a data pipeline you can safely re-run?hardAlso on schema-evolution6 min
- Your service writes a record, reads it straight back, and sometimes gets the old value. What is happening and what do you change?hardAlso on replication5 min
- How do you turn what the business tells you into a domain model that holds up once the exceptions arrive?hardAlso on bounded-context7 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 schema-evolution6 min
- When would you use the observer pattern, and what goes wrong with it at scale?mediumAlso on coupling4 min
- Give me a real single-responsibility violation you have seen, and how would you refactor it?mediumAlso on coupling5 min