skip to content

What is Separation of Concerns in software design, and what is a "concern"?

level: juniorimportance: must knowfreq 78%

answer

  1. concern = distinct area of interest
  2. Dijkstra 1974, "focus attention on one aspect"
  3. one change → one place
  4. high cohesion, low coupling
  5. SRP = class-level instance of SoC

basics

~20 s

Separation of Concerns means splitting a system so each part deals with one distinct topic — for example, storing data, applying business rules, or drawing the screen — instead of mixing them all in one place.

solid answer

~40 s

A "concern" is any distinct area of interest a program must address: persistence, validation, business rules, presentation, authorization, logging, error handling. Separation of Concerns (a term popularized by Edsger Dijkstra in 1974) says the system should be partitioned so that each module addresses one concern, and knowledge of one concern is not scattered across many modules. The practical payoff is localized change: if the database engine changes, only the persistence code changes; if a pricing rule changes, only the domain code changes. It is applied at every scale — a function, a class, a layer, a service, even a file (HTML/CSS/JS). SoC is the general principle; Single Responsibility Principle, layering, modularity, encapsulation and information hiding are specific applications of it. Its measurable symptoms are high cohesion inside a module and low coupling between modules.

go deeper

for a junior

Define concern with concrete examples (UI, business rules, database) and say why mixing them hurts: changes and tests get tangled.

for a middle

Add the mechanism — cohesion/coupling, interfaces, and how layering/MVC embody SoC; distinguish SoC from SRP.

for a senior

Discuss choosing the separation axis by what changes together, leaky abstractions, cross-cutting concerns, and the cost of over-separation.

for a principal

Frame SoC as risk and change-cost management (Parnas information hiding), tie module boundaries to team boundaries and rate of change, and discuss when to deliberately keep concerns together.

## The idea A **concern** is a distinct area of interest or responsibility inside a software system — a thing the system must take care of. Typical concerns: - **Presentation** — how information is shown to a user or returned over an API. - **Business/domain logic** — the rules that define what the software actually means (e.g. "an order over $100 gets free shipping"). - **Persistence** — how data is stored and retrieved (SQL, files, key-value store). - **Cross-cutting concerns** — things needed almost everywhere: logging, authentication/authorization, transactions, caching, metrics, retries, input validation. **Separation of Concerns (SoC)** is the design principle that a system should be decomposed so that each part addresses one concern, and each concern is addressed in as few places as possible. The term is attributed to **Edsger W. Dijkstra**, in his 1974 essay *On the role of scientific thought*, where he described "focusing one's attention upon some aspect" — not ignoring the others, but studying one at a time because the human mind can only reason about so much at once. ## Why it matters (the real payoff) SoC is not aesthetic tidiness; it buys concrete properties: 1. **Localized change.** A change to one concern touches one place. Swapping the database ideally touches only persistence code. This is the same argument as David Parnas's 1972 "information hiding": modularize around *what is likely to change*, so that change is contained. 2. **Independent reasoning.** You can understand pricing rules without understanding SQL connection pooling. 3. **Independent testing.** Business rules can be unit-tested without a database or an HTTP server, because they don't reference either. 4. **Independent replacement and reuse.** The same domain logic can serve a web UI, a CLI, and a batch job. 5. **Parallel work.** Different people/teams can work on different concerns with fewer conflicts. ## What "separated" actually means Separation is not merely putting things in different files. Two modules are truly separated when **neither needs to know the internals of the other**, i.e. they interact through a narrow, stable interface. Two signals: - **Cohesion** (high, good): everything inside a module relates to the same concern. - **Coupling** (low, good): a module depends on few others, and only through explicit contracts. If you move database calls into a separate class but the domain code still constructs SQL strings and passes them in, you have relocated code without separating the concern. ## Scales at which SoC applies | Scale | Example of separation | |---|---| | Statement/function | Extract a validation loop out of a rendering function | | Class | An `Invoice` holds amounts and rules; an `InvoicePrinter` formats them | | File | HTML (structure) / CSS (presentation) / JS (behavior) | | Layer | UI → application → domain → infrastructure | | Process/service | Payments service vs. catalog service | | Deployment/ops | Application code vs. configuration vs. secrets | ## Relationship to other principles - **Single Responsibility Principle (SRP)** — the class-level (Robert C. Martin phrases it as "one reason to change", i.e. one *actor* requesting change) application of SoC. SoC is broader: it applies to functions, files, layers and whole systems, and it also covers concerns that span many classes. - **Modularity / encapsulation / information hiding** — the mechanisms by which separation is enforced. - **Layered architecture, hexagonal/ports-and-adapters, MVC/MVP/MVVM** — architectural patterns whose whole purpose is to separate specific concerns (domain from I/O; model from view). - **Aspect-Oriented Programming, middleware, decorators, interceptors** — techniques for separating *cross-cutting* concerns that would otherwise be duplicated in every module. ## Trade-offs and failure modes SoC is not free, and "more separation" is not automatically better: - **Indirection cost.** Each boundary adds a hop. Reading a feature end-to-end may mean opening six files. Excessive layering ("lasagna code") makes trivial changes touch many artifacts. - **Wrong axis of separation.** If you split by technical layer but every feature change touches all layers, you have separated along an axis that doesn't match your change patterns. Separation should follow *what changes together*. - **Anemic separation.** Pushing all logic out of the domain into "services" separates code but destroys cohesion — the classic anemic domain model. - **Leaky abstraction.** A repository interface that exposes database-specific query semantics hasn't separated persistence; the concern leaked through the interface. - **Premature separation.** Splitting a concern you haven't yet understood tends to produce boundaries in the wrong place, which are then expensive to move. A useful test: *when requirement X changes, how many modules must I open?* If the answer is one, the concerns matching X are well separated. If the answer is "one per layer, every time", the layering is bureaucracy rather than separation. ## Concrete before/after A function that (1) parses an HTTP request, (2) validates it, (3) computes a discount, (4) writes SQL, and (5) formats HTML mixes five concerns: any change to the discount rule forces you to read HTTP and SQL code, the discount rule cannot be tested without a database, and the same rule will likely be copy-pasted into the batch job. Splitting it into a controller (transport), a validator, a domain service (rule), a repository (persistence), and a view (presentation) means the discount rule now exists once, is testable in isolation, and is reusable across entry points — at the cost of five artifacts instead of one.

  • How is Separation of Concerns different from the Single Responsibility Principle?
    SRP is a specific, class-scoped application of SoC — a class should have one reason (one actor) to change. SoC is the broader principle applied at every scale: function, file, class, layer, service, deployment. SoC also covers cross-cutting concerns that no single class owns, which SRP doesn't directly address.
  • Is putting code into separate files enough to separate concerns?
    No. Separation requires that neither part depends on the internals of the other. If the domain file still builds SQL strings or knows HTTP status codes, the concern is still entangled — you've only moved text. The test is whether one concern can change (or be replaced/tested) without the other.
  • Can you over-apply Separation of Concerns?
    Yes. Every boundary adds indirection: more files, more interfaces, harder end-to-end reading. If a typical requirement change forces edits in every layer, the separation axis doesn't match your change patterns and is pure overhead. Separate along what actually changes independently.

A restaurant separates concerns: the kitchen cooks, waiters serve, the cashier handles money. Changing the menu doesn't change how payments work, and the chef can be replaced without retraining the cashier. Put all three jobs in one person and every change retrains everybody.

saying these in an interview costs you the question

  • Saying SoC just means "having lots of small files/classes" — separation is about dependency and change, not file count.
  • Claiming SoC and SRP are exactly the same thing.
  • Believing more layers is always better design, ignoring indirection cost.
  • Calling code separated when the "domain" module still contains SQL, HTTP status codes, or UI formatting.
  • Treating SoC as an object-oriented-only idea — it applies to functions, files, modules, services, and even config.

context