concept

Separation of Concerns

Organising a system so that one kind of change touches one place, and so that different concerns can fail and scale independently.

designmodularityboundaries

Separation of concerns is usually taught as a code-organisation idea. Architecturally it is sharper: two concerns should be separated when they have different rates of change, different failure requirements, or different scaling profiles. Anything else is decoration.

The three real tests

  • Change rate. Pricing rules change weekly; the payment ledger changes yearly. Coupling them means every pricing tweak risks the ledger.
  • Failure requirement. Serving an existing document and accepting a new upload have different consequences when broken. Keeping them in one failure domain means the read path dies with the write path.
  • Scaling profile. A component that needs 64 GB of RAM per instance should not be in the same deployable as one that needs 512 MB, because you will provision the maximum everywhere.

Industry example

GitHub separates concerns along an unusually clear line: Git object storage, which is write-once and enormous, sits behind a very different system from the collaboration layer — issues, pull requests, reviews, permissions — which is relational, highly interactive, and changes constantly. Layered on top is an asynchronous concern: webhooks, CI runs, notifications and search indexing, all triggered by repository events and none of which should block a git push.

That third separation is the load-bearing one. If indexing a repository ran inline with the push, one pathological repository would degrade pushes for everyone. Moving it to background jobs means the concern degrades independently: search can be minutes stale while pushes stay fast, which is the right failure for the product.

Failure scenario

The most common violation is a synchronous side effect that does not need to be synchronous — sending an email, writing an audit record to a remote system, updating a search index — inside the request path. Every one of those imports another system's availability into yours, for a concern the user is not waiting on.

Trade-off

Separation costs coordination. Splitting the indexing concern out means accepting stale search and building a way to detect and repair drift. That is a real cost, paid for a real benefit; the mistake is separating concerns that share all three tests and gaining nothing but hops.

Interview question

"Which side effects in a typical 'user signs up' flow belong in the request path, and which belong outside it? Justify each."