Concept · Code design

Command–query separation

A method should either answer a question or change state, not both — so asking a question never changes the answer.

2 minCQSside effectsquery methods

Command–query separation (CQS), introduced by Bertrand Meyer, divides every method into one of two kinds:

  • A query returns information and has no observable side effects.
  • A command changes state and returns nothing except, in Go, an error.

Meyer's summary: asking a question should not change the answer.

Why it matters

When queries are side-effect free, you can call them anywhere — in a log line, a debugger, an assertion, a retry — without changing behaviour. A query with a hidden side effect breaks that trust:

go
// Looks like a question, but consumes the token
func (s *Store) IsValid(token string) bool

Someone logs store.IsValid(tok) before the real check, and every login fails. Splitting it into IsValid (query) and MarkUsed (command) makes both safe and obvious. Commands that don't return data also make it clear at the call site that something is changing.

Justified exceptions

Some operations must check and change in one atomic step, or concurrent callers could both pass the check:

  • Pop() on a stack returns and removes the top item.
  • UPDATE ... RETURNING changes rows and reports them.
  • Compare-and-swap, INSERT ... ON CONFLICT DO NOTHING RETURNING id, or "claim the next job" with SKIP LOCKED.

Combining is correct there. Name such methods as commands (Pop, Claim, Consume, Reserve) so nobody mistakes them for a harmless query.

CQS vs CQRS

CQRS — command query responsibility segregation — applies the idea at the architectural level: separate models, and sometimes separate data stores, for writes and reads. It can pay off when read and write shapes differ greatly, but adds real complexity; see event sourcing and CQRS. CQS is the cheap, everyday habit; CQRS is a deliberate architecture decision. The design principles lesson covers both.

Learn it in depth