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.
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:
// Looks like a question, but consumes the token
func (s *Store) IsValid(token string) boolSomeone 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 ... RETURNINGchanges rows and reports them.- Compare-and-swap,
INSERT ... ON CONFLICT DO NOTHING RETURNING id, or "claim the next job" withSKIP 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.