Command-Query Separation (CQS) is Bertrand Meyer's design rule from Object-Oriented Software Construction (1988): every method is either a command, which changes state and returns nothing, or a query, which returns a value and changes no observable state, never both. Asking a question then does not change the answer, so queries are safe to repeat and to call from contracts and assertions. It is a per-method rule, distinct from the architectural pattern CQRS.
CQS (Command-Query Separation)
Details
- Full Name
-
Command-Query Separation, from Bertrand Meyer’s Object-Oriented Software Construction (1988), developed alongside Eiffel
- Also known as
-
CQS, command-query separation; Fowler prefers "modifiers" over "commands", and the term "mutators" is also current
Core Concepts:
- One Rule, Applied Per Method
-
Every method is either a command, which changes state and returns nothing, or a query, which returns a value and changes no observable state. Never both. Fowler states the two halves: "Queries: Return a result and do not change the observable state of the system (are free of side effects). Commands: Change the state of a system but do not return a value."
- Asking a Question Does Not Change the Answer
-
The payoff is that a query can be called twice, in any order, from a logger or a debugger or an assertion, without moving the system. Reading a signature tells you whether the call is safe to repeat — which is exactly the property a reader cannot otherwise recover without reading the body.
- This Is Not CQRS
-
cqrsis an architectural pattern: separate read and write models, often separate stores, usually eventual consistency between them. CQS is a rule about a single method signature, and it applies in a 300-line codebase that will never have two models. Greg Young and Udi Dahan lifted Meyer’s method-level rule to architecture; the lift does not replace the original. - The Design-by-Contract Motivation
-
design-by-contractis the same author’s work and the reason CQS exists. Pre-conditions, post-conditions and invariants call queries. If a query mutated, writing a contract would change the thing the contract describes, and assertions could not be compiled out of a release build. - Side Effects Versus Observable Side Effects
-
A query may fill a cache, increment a hit counter or open a connection. CQS asks about observable state — what a caller can detect through the object’s own interface. Memoisation is therefore compatible with the rule, not an exception to it.
- Where the Rule Bends
-
pop(),next()on an iterator and atomic compare-and-set all mutate and return, deliberately. Fowler: "Popping a stack is a good example of a query that modifies state. Meyer correctly says that you can avoid having this method, but it is a useful idiom. So I prefer to follow this principle when I can, but I’m prepared to break it to get my pop." Concurrency makes the case stronger: splitting compare-and-set into a query plus a command introduces the race the atomic operation exists to close. - The Diagnostic Use
-
CQS is a reading tool as much as a writing rule. Go through an interface and sort the methods into commands and queries; the ones that resist sorting are where the design is unclear, which makes it a specific lens on
code-smells.
When to Use:
-
Designing or reviewing a class or module API, when a reader must see from the signature whether a call is safe to repeat
-
Sorting an existing interface: any method that both mutates and returns is a question to answer, not automatically a defect
-
Wherever assertions, contracts or invariants will call into the code (
design-by-contract) -
Writing code a model will extend: a command/query split survives being read out of context better than a convention in the docstring
-
Naming the deliberate exceptions, so
pop()is a decision in the record and not an oversight -
Per slice in
vertical-slice-architecture, where the command/query split is the natural seam before any CQRS-scale separation
Common Misunderstandings:
-
❌ "CQS and CQRS are the same thing, one is just the abbreviation" — CQS is Meyer’s per-method rule from 1988; CQRS is the architectural pattern with two models
-
❌ "CQS means you need separate read and write databases" — that is CQRS; CQS costs nothing architecturally and needs no second store
-
❌ "A query must have no side effects at all" — it must have no observable ones; caching and counters are fine
-
❌ "The rule is absolute" — Fowler breaks it for
pop()on purpose, and atomic compare-and-set cannot obey it without reintroducing a race -
❌ "A command may return a status code" — returning an error or a status is the usual, defensible bend; returning the created entity’s ID is the contested one
-
✓ "Every method is a command or a query, and the exceptions are named" — the rule plus its named exceptions is the whole practice
Criticism:
-
The author of the best-known modern write-up refuses to follow it absolutely. Martin Fowler’s bliki entry credits Meyer — "The term 'command query separation' was coined by Bertrand Meyer in his book 'Object Oriented Software Construction'" — endorses the value, "it’s extremely handy if you can clearly separate methods that change state from those that don’t", and then declines the pure position: "So I prefer to follow this principle when I can, but I’m prepared to break it to get my pop." Fowler, "CommandQuerySeparation" (5 December 2005).
-
The database-generated ID is the standing counterexample, and it has a cost either way. Mark Seemann names the collision: a repository method that saves a new entity and returns the ID the database assigned. "The problem with this design, obviously, is that it violates CQS. The Create method ought to be a Command, but it returns a value." His fix is to pass the ID in —
void Create(Guid id, T item)— and he concedes the price: "Most software design decisions involve trade-offs, and so does this one. You gain better encapsulation for potentially worse performance." Seemann, "CQS versus server generated IDs" (11 August 2014). -
Vladimir Khorikov answered that the price is too high. Replying to Seemann directly: "While this solution clearly solves the problem of violation CQS, it does it at a high cost." His concrete objection is not aesthetic: "Besides the fact that this design results in two calls instead of one, GUIDs hurt performance of inserts." Khorikov, "CQS with Database-Generated Ids" (15 November 2014). The exchange is the most useful criticism available, because both sides accept the rule and disagree only about what obeying it costs.
-
No named German-language critic found. Searched in English and German for documented objections to CQS as a method-level rule. The German-language material that surfaces is introductory or translated — definitions and tutorials, no citable critic. Treat this as a search result with its frame attached, not as evidence that none exists; the term is normally discussed in English.
Current Status:
-
CQS stayed a method-level rule while CQRS took the attention. The acronym most teams meet first is CQRS, and the method-level original is often assumed to be its abbreviation. Fowler’s 2005 entry remains the common modern reference for the original rule, and the 2014 Seemann/Khorikov exchange is still where the hard case is argued.
-
The bare acronym is safe, and naming it partitions an interface. Measured on
claude-haiku-4-5in a clean room on 2026-10-03: asked how a developer should know, before calling one of twelve methods, whether the call is reversible — a task carrying none of CQS’s vocabulary — the baseline answered with JavaDoc annotations and a naming convention. Naming CQS partitioned the twelve into queries that change nothing and commands that return void. The bare acronym also holds: asked "What is CQS?", the weak tier returned Meyer’s rule without drifting to CQRS.