2.2.9 · D5Design Principles

Question bank — Separation of concerns

1,665 words8 min readBack to topic

Before we start, six words the questions lean on (restated so line one makes sense):

Two pictures anchor almost every trap below.

Picture 1 — the two forces you are trading off. Coupling (how many others a module depends on) versus cohesion (how focused a module is on one concern). SoC aims for the corner that is low coupling, high cohesion; the failure modes sit in the other corners.

Figure — Separation of concerns

Picture 2 — layering as SoC made structural. The three failure/success shapes: tangling (one box, many jobs), scattering (one job smeared across boxes), and clean layers whose dependency arrows all point one way (Presentation → Domain → Data), so a database swap never forces a UI rewrite.

Figure — Separation of concerns

True or false — justify

TRUE or FALSE: Splitting one file into ten files automatically separates concerns.
False. File count is not separation. If a single business change forces edits across all ten, the concern was scattered into fragmentation, not separated.
TRUE or FALSE: A module with zero coupling is always well-designed.
False. You can hit zero module coupling by dumping everything into one giant module — but then cohesion collapses (bottom-right corner of Picture 1). Both forces must be won together (see Coupling and Cohesion).
TRUE or FALSE: "One concern" means "one line of code" or "one tiny function".
False. One concern = one reason to change, which can span many lines. Equating it with size causes over-splitting into fragmentation.
TRUE or FALSE: Adding more layers always improves separation.
False. Unnecessary layers add indirection and coupling for a tiny script. You add a boundary only when two concerns genuinely change independently (Layered Architecture).
TRUE or FALSE: Logging spread through every function is a well-separated concern.
False. Logging touching every module is scattering. As a cross-cutting concern (one job smeared across all layers) it should be isolated via a decorator/middleware so business code stays clean (Cross-cutting concerns).
TRUE or FALSE: If two pieces of code change for the same reason, they should be split apart.
False. Same reason to change → they belong together. You split apart only what changes for different reasons.
TRUE or FALSE: High cohesion means a module never depends on anything else.
False. Cohesion is about internal focus (elements serving one concern); dependence on others is coupling, the separate axis in Picture 1. A cohesive module can still have a few necessary dependencies.
TRUE or FALSE: Separation of Concerns (SoC) and the Single Responsibility Principle describe the same underlying idea.
True. "List the reasons to change, one per module" is literally SRP; SoC is the same idea applied at any scale from function to layer.
TRUE or FALSE: The Data → Domain → Presentation dependency arrows can point either way as long as the layers exist.
False. The one-directional flow (Presentation → Domain → Data, as drawn in Picture 2) is what lets a database swap avoid a User Interface (UI) rewrite. Reversing an arrow re-tangles the concerns.
TRUE or FALSE: Making functions "pure" (no database, no screen) is unrelated to SoC.
False. Extracting pure business logic (like cart_total) is separation — it isolates the calculation concern so it's testable without storage or presentation.

Spot the error

Claim: "I put validation, DB access, and formatting all in save_user() but it's fine — it's only one function." What's the error?
One function ≠ one concern. Three reasons to change (validation rules, DB tech, output format) live in one place, so any one change risks the others. This is tangling (the single overloaded box in Picture 2).
Claim: "My cart_total calls fetch_cart inside itself to always get fresh data." What's the error?
That re-couples the business concern to the storage concern. Now cart_total can't be tested without a database, undoing the separation. Pass data in as an argument instead.
Claim: "I separated concerns by making 50 tiny modules that each call five others." What's the error?
Coupling exploded — this is fragmentation. Cohesion may look high per module, but the chatty web means a change ripples everywhere, the opposite of keeping change local (fragmentation).
Claim: "Same SQL query is copy-pasted in three services — that's fine, they're separate concerns." What's the error?
Duplicated logic means the storage concern is scattered across three places; one schema change edits three spots. This violates Don't Repeat Yourself (DRY) and signals a missing shared boundary.
Claim: "The User Interface (UI) layer reads directly from the database to be faster." What's the error?
This skips the domain layer and couples presentation to storage — an arrow pointing the wrong way in Picture 2. A DB change now forces a UI rewrite, the exact tangling layering exists to prevent.
Claim: "I exposed the module's internal list directly so callers can tweak it." What's the error?
That leaks internals and breaks the narrow interface. Callers now depend on implementation details, raising coupling — the fix is Information Hiding behind a small interface.

Why questions

Why is "how many places do I edit for a typical change?" the real test of good SoC?
Because SoC's goal is keeping change local. If one reason-to-change touches many places, the concern was scattered regardless of how tidy the file structure looks. Ideally the answer is one.
Why must coupling and cohesion be optimized together rather than one at a time?
Each is individually gameable — one mega-module fakes low coupling, many chatty modules fake high cohesion. Only the low-coupling/high-cohesion corner of Picture 1 wins both, which is what Modularity and SoC aim for.
Why do we isolate logging/auth with decorators instead of writing them inline?
They are cross-cutting concerns (one job that would otherwise smear across every layer). A decorator keeps the concern in one home while still running alongside the business logic (Cross-cutting concerns).
Why does grouping "by data" instead of "by reason to change" go wrong?
Data that looks related can change for different reasons (storage format vs. business rule), so grouping by data re-tangles concerns. Grouping by reason keeps things that change together, together.
Why is a pure business function (no DB, no UI) a sign of successful separation?
Purity means the calculation concern has no hidden dependency on storage or the User Interface, so it can be tested and changed in isolation — change stays local.
Why does over-splitting into micro-functions reduce cohesion in practice?
When one logical concern is chopped into fragments spread across many units (fragmentation), understanding it requires reading all of them — the concern is scattered, so the "cohesion" of any one fragment is illusory.

Edge cases

For a 20-line throwaway script, is SoC into four layers a good idea?
No. There are no real independent seams to cut, so layers add ceremony and coupling with no payoff. Add a boundary only when two concerns actually change independently.
Is a system with exactly one module ever acceptable under SoC?
Yes, when there's genuinely one concern. If nothing changes for a second, independent reason, splitting would only add coupling. SoC cuts real seams, not imaginary ones.
Two functions change for the same reason but sit in different modules — separated or not?
Not properly separated — it's scattering. They share a reason to change, so they belong in one home; being in different files just spreads that single concern out.
A concern (like error handling) legitimately touches every layer — does that break layering?
No. Cross-cutting concerns are expected; you don't force them into one layer, you factor them out orthogonally (middleware/decorator) so each layer's own concern stays clean.
If applying SoC makes a tiny program harder to read, what does that tell you?
You likely cut along a fake seam or over-fragmented into fragmentation. SoC should make change local and reading easier; when it doesn't, the boundary wasn't a real concern boundary.
Can two concerns share the same variable and still be considered separated?
Yes, if they touch it only through a narrow interface. Separation is about independent reasons to change and hidden internals, not about never sharing any data (Information Hiding).