Skip to content

CONCEPT Cited by 8 sources

Separation of concerns

Definition

Separation of concerns is the practice of organising a system so that each component has a single, well-defined responsibility, and components interact only through explicit interfaces. A change inside one concern should not ripple into others. The term is due to Dijkstra (1974) but the principle is older than the word.

At practical altitudes it surfaces as:

"Separation applies to the whole system"

A recurring insight in production-engineering retrospectives is that separation of concerns applies to everything, not just application code. Slack's Quip/Canvas team articulated this as the core lesson of their 60 min → 10 min build refactor:

Once we understood the problem through the lens of separation of concerns, it became clear we couldn't succeed with changes to the build system alone. We had to cleanly sever the dependencies between our frontend and backend, our Python and TypeScript, our application and our build.

— Slack, Build better software to build software better

And:

The whole system is more than our application code. It's also our build code, our release pipeline, the setup strategies for our developer and production environments, and the interrelations between those components.

Slack identified three couplings that each violated separation of concerns and together produced a 60-minute build:

  1. Backend ↔ frontend: the Python backend's built artifacts were transitive inputs to every TypeScript-frontend bundle, so every Python change invalidated every frontend cache entry.
  2. Python ↔ TypeScript toolchains: Python scripts orchestrated tsc and webpack, fusing two unrelated languages' build pipelines and forcing them to share state.
  3. Application code ↔ build code: build logic was in Python, imported application modules, and ran inside the same process — so a refactor of an application module could break the build, and vice versa.

Each of the three couplings had both correctness and performance consequences.

Why separation of concerns improves performance (not just maintainability)

Slack's refactor is a good case study because the performance lesson is often underweighted relative to the maintainability lesson:

  • Cache hit rate is higher when fewer inputs leak into each unit of work — separation of concerns makes explicit which inputs actually matter. (See cache-granularity.)
  • Parallelism is higher when units of work don't share mutable state — separation lets a scheduler run them concurrently.
  • Blast radius is smaller when a change to one concern cannot reach another — engineers can reason about the consequences of their changes, which itself accelerates velocity.

The "whole system" inventory

The Slack framing is worth lifting verbatim as a reminder of which concerns deserve separation discipline:

Concern Typical artefacts
Application code business-logic source, app binaries
Build code BUILD files, Makefiles, build rules
Release code deploy scripts, rollback procedures
Setup code Terraform, Ansible, provisioning scripts
Test code fixtures, harnesses, mocks
Observability code instrumentation, dashboards, alerts

Each should be authored, versioned, and evolved with its own lifecycle; couplings between them should be explicit and minimal.

Anti-patterns

  • Build code imports application code. Tempting when both are in the same language; creates a coupling where app refactors break the build. Slack's rewrite-in-Starlark is a defence against this.
  • Deploy scripts with embedded business logic. The release pipeline should not know why a deploy is happening; the application should not know how it's being deployed.
  • Test fixtures referencing production configuration. Makes tests brittle to config changes unrelated to what they verify.
  • One repository, no boundaries. Monorepos are fine, but monorepos without explicit intra-repo separation of concerns (via build graphs, visibility rules, ownership files) collapse into balls of mud.

Seen in

  • sources/2026-09-29-yelp-the-making-of-the-yelp-assistant-ui — view/data separation in a server-driven UI. Yelp Assistant splits the concern of what the UI looks like (the CHAOS view configuration, fetched once as a template) from what content fills it (MessageEvents streamed turn-by-turn into datasets). The renderer is agnostic to message type: "Whether the client renders a user message, a typing indicator or something completely new does not matter, as CHAOS abstracts it away." This decoupling is what lets new message/feature types ship server-side without a client release — a client-architecture instance (backend-frontend-network-separation) alongside the build-code, pipeline, and synthetic-data instances below.
  • sources/2026-09-29-dropbox-evolving-our-calendar-assistant-reclaim-to-be-ai-native-with-f60d451f — one operation type shared by three actors ("Schedule Actions"). Reclaim adding an AI agent to an existing calendar assistant is a textbook separation-of-concerns move: it represents each scheduling capability (create/update event, change RSVP, find availability) as a Schedule Action — a single operation type with one validation + commit path — and routes all three actors (the user's explicit edits, the automated background scheduler, and the new agent) through it. The operation and its correctness rules are one concern, decoupled from who invoked it. The payoff is stated precisely: because a calendar change ripples (moving a meeting alters availability and can force adjustments elsewhere), separate per-actor implementations would mean reproducing and re-aligning those rules forever — with a shared Schedule Action, "engineers can change how an operation works in one place rather than updating three separate versions." A single-source-but-canonical instance of unify the execution path, vary only the caller — tagged for Lint promotion.
  • sources/2025-11-06-slack-build-better-software-to-build-software-better — canonical articulation that separation of concerns applies to build code, release code, and setup code, not just application code; three specific couplings (backend↔frontend, Python↔TypeScript toolchain, application↔build) producing a 60-minute build and quantitative impact on blast-radius reasoning.
  • sources/2026-07-09-aws-specification-driven-composition-for-flexible-data-workflows — pipeline-level instantiation: separates workflow intent (declarative specification), composition (validation + assembly), and processing (reusable Lambda capabilities) into three independent layers; enables regulated-environment separation of duties where business users author intent but cannot modify execution code.
  • sources/2026-09-21-atlassian-how-atlassian-built-a-scalable-synthetic-data-engine — separation-of-concerns applied to synthetic data generation: Data Brewery's two-phase generation model decouples row generation (Phase 1: all non-FK fields, generated independently and in parallel) from relationship resolution (Phase 2: a dedicated FK injector links rows in dependency order using only existing parents). The insight was empirical — FK-free tables generated fast, and each added foreign-key dependency slowed generation dramatically — so the expensive relational coupling was pulled out of the throughput-sensitive hot path into a separate pass. Data Brewery also pushes the volatile part (entities, shapes, relationships) into declarative config rather than code, a config-over-code separation that turns a per-entity engineering bottleneck into a self-serve knob.
  • sources/2026-09-21-meta-open-sourcing-rebalancer-a-generic-high-performance-library — solver-level separation of concerns: Meta's Rebalancer separates how an assignment problem is specified (objects / bins / constraints / objectives via a declarative spec language) from how it is solved (compile to an expression-graph IR, then pick a mixed-integer-programming or local-search backend). Meta calls this separation "crucial to Rebalancer's usability, scalability, and extensibility" — the same specification can be solved by an exact MIP for prototyping and by parallelized local search at hyperscale without rewriting the model.
  • sources/2026-09-24-atlassian-how-we-automated-feature-flag-cleanup-with-agentic-pipelines — agent-prompt-level separation of concerns: the "thin prompt, rich skill" split. Atlassian keeps the coding-agent prompt deliberately thin (a small harness that loads a skill and reports a one-line result) so that "almost none of the domain logic lives in the prompt itself. Everything lives in the skill" — an explicit decision "because we did not want flag-cleanup knowledge scattered across pipeline configuration and prompt text." The consequence is reuse across contexts: "the same skill works whether it's triggered by the pipeline or run interactively by a developer" — the versioned skill is the single authoritative home for the domain concern. See patterns/dispatcher-coding-agent-closer.
  • sources/2026-09-24-databricks-how-i-built-agent-based-security-reviews-on-databricks — agent-level separation of concerns: a security-review system built as seven agents each with one bounded job (intake, risk assessment, requirements, specialized review, validation, workflow, learning) rather than one general-purpose "security reviewer" agent. The stated payoff is exactly the separation-of-concerns payoff — "each agent has bounded responsibility, so its behavior remains inspectable and testable, and a change to one does not silently affect another." This is the reasoning-agent analogue of module-level single-responsibility: narrow scope makes each unit independently testable and prevents cross-coupling. See patterns/specialized-agent-decomposition.
Last updated · 766 distilled / 2,225 read