skip to content

A colleague proposes introducing CQRS — separate write and read models — for a new internal admin tool that has low traffic and simple CRUD screens. What's your reasoning for pushing back, and what are the concrete signals that would change your mind?

level: seniorimportance: must knowfreq 70%

answer

  1. resume-driven architecture warning
  2. single model = simpler until proven otherwise
  3. need a measurable asymmetry, not 'might scale someday'
  4. cost is ongoing and sticky, hard to unwind
  5. contrast: admin CRUD vs. high-read catalog

basics

~20 s

If reads and writes are low-volume and shaped similarly, splitting into two models just adds complexity (sync pipeline, staleness, more code) with no real benefit — you'd push back and ask what specific problem it's meant to solve.

solid answer

~40 s

CQRS's cost — dual models, an async sync mechanism, eventual consistency, doubled operational surface — is only worth paying when there's a real, specific asymmetry it solves: very different read/write scaling ratios, read query shapes that genuinely can't be served well by the write model's schema, or a need to scale reads independently of writes. A low-traffic admin CRUD tool typically has none of that: one model can serve both simple reads and writes fine, with room to grow via caching or read replicas long before a full model split is justified. Signals that would change the reasoning: the reads start needing a fundamentally different shape (search, complex aggregation) than the write schema naturally provides, or read and write load diverge sharply enough that scaling them together becomes wasteful or a bottleneck.

go deeper

for a junior

Should sense that adding two models for a small, low-traffic tool feels like overkill, even without being able to name the specific alternatives.

for a middle

Should name at least one lighter-weight alternative (read replicas, caching) and explain why it's cheaper for this case.

for a senior

Should articulate the specific measurable signals that would flip the decision, and push back on the proposal with concrete reasoning rather than a vague 'let's keep it simple.'

for a principal

Should set org-level guidance (e.g. an architecture decision record or default guidance) on when CQRS is justified, to prevent this decision from being re-litigated ad hoc project by project, and weigh the sunk, sticky cost of unwinding it later.

## The most common mistake The single most common CQRS mistake in the field isn't misusing it once adopted — it's adopting it where a single model would have been simpler, cheaper, and just as correct. Pushing back on CQRS for a low-traffic admin CRUD tool is really an application of a general engineering principle: **architectural complexity should be introduced to solve a demonstrated problem**, not spent pre-emptively on the chance it might be needed. CQRS specifically trades simplicity for scalability and flexibility along the read/write axis, and if that axis isn't under strain, the trade is pure cost with no offsetting benefit. ## What one shared model already gives you Concretely, a single shared model handles the common case well: - one schema, one set of classes/entities, one database; - straightforward transactional consistency (a read immediately after a write sees the write — no eventual-consistency window to reason about); - one thing to test, deploy, and operate. For an admin tool with simple CRUD screens and low traffic, this is very likely sufficient for the tool's entire lifetime — the query patterns (list records, filter by a couple of fields, edit one record) are exactly what a normalized relational schema serves well natively, without any denormalization or projection needed. Reaching for CQRS here means paying its costs — building and maintaining a second model, standing up a sync mechanism, teaching the team to reason about staleness, doubling the operational surface described elsewhere in this topic — for zero measurable benefit, since there's no asymmetry or scaling pain for it to fix. This is sometimes called 'CQRS as **resume-driven architecture**': applying an interesting pattern because it's interesting, not because the system needs it. ## The subtler cost There's also a subtler cost worth naming to a colleague: **cognitive load and hiring/onboarding friction**. Every engineer who touches this admin tool now has to understand two models and an async sync boundary, even for a simple bug fix, which slows down exactly the kind of small internal tool that's supposed to be cheap to maintain. And CQRS is not free to unwind either — once a read model has downstream consumers (reports built against it, other services querying it), collapsing back to a single model is its own migration project, so the cost isn't just paid once at adoption but ongoing and sticky. ## The signals that would change the answer The signals that would legitimately justify introducing CQRS — and that a senior engineer should be listening for — are specific and measurable, not vague ('it'll scale better someday'). 1. **First**, a real, demonstrated asymmetry in read/write volume or ratio: if reads start dramatically outpacing writes (say, orders of magnitude) such that read traffic is causing contention or forcing over-provisioning of write-side infrastructure just to serve reads. 2. **Second**, a genuine shape mismatch: the read side needs a fundamentally different query shape than the write schema naturally supports — full-text search, complex multi-entity aggregation/reporting, or a completely different access pattern (e.g. time-series rollups) that would require either expensive JOINs against the write schema or denormalizing the write schema itself (which pollutes it and slows writes). 3. **Third**, a need to scale reads and writes independently — for instance, wanting to run read replicas in multiple regions for latency while keeping writes centralized for consistency, which is easier to reason about with an explicit read/write split. ## The contrasting case A worked example of the right call: the same team also owns a customer-facing product catalog with millions of reads a day (browsing, search, filtering) against a write side that only changes when merchandisers update a few hundred products a day. Here the asymmetry is obvious and severe, the read shape (faceted search, filtering) is a poor natural fit for the write schema, and the two sides have wildly different scaling needs. That's a legitimate CQRS candidate — worth the sync pipeline and eventual consistency, because customers browsing slightly stale search results is an acceptable trade for a system that can actually handle the load. The contrast between the two cases — reject for the admin tool, adopt for the catalog — is the core senior-level judgment call this topic tests: not whether CQRS is 'good' or 'bad' in the abstract, but whether the specific system in front of you has the asymmetry that makes its cost worth paying. A senior engineer should be able to articulate this trade-off explicitly to a colleague rather than either rubber-stamping the proposal or rejecting it on gut feel alone.

  • If the admin tool later does grow — say, a reporting feature that needs heavy aggregation — what would you reach for before jumping straight to full CQRS?
    Cheaper intermediate steps first: a read replica of the same database for isolating heavy read queries from write traffic, or a scheduled materialized view / rollup table refreshed periodically for the reporting queries specifically. Both solve read/write contention or shape mismatch without the full cost of a separately maintained model and event-driven sync pipeline, and can be adopted incrementally for just the pain point rather than the whole system.
  • Is it possible to apply CQRS to only part of a system rather than all-or-nothing?
    Yes — CQRS is commonly applied per-aggregate or per-bounded-context rather than system-wide; a team might keep a simple single model for most entities and introduce a read projection only for the one high-read, shape-mismatched entity (like the product catalog). This bounds the cost to where the benefit actually is, which is usually the right default over an all-or-nothing adoption.

Like installing a commercial dual-zone HVAC system in a one-room studio apartment because a mansion down the street benefits from zoned climate control — the pattern is real and valuable somewhere, just not for a room with one set of needs and one thermostat's worth of load.

saying these in an interview costs you the question

  • Advocates CQRS for every new service by default
  • Can't articulate what specific problem CQRS solves in this case
  • Dismisses the eventual-consistency and operational cost as negligible
  • Doesn't consider cheaper alternatives (read replicas, caching, materialized views) before proposing a full model split
  • Treats 'it might need to scale eventually' as sufficient justification without evidence

context