Skip to content

04 · Together, apart, or push complexity down

When you finish this lesson you should be able to:

  • Decide when two ideas belong in one module versus two
  • Push messy details behind a stronger module instead of spraying them upward
  • Produce 6–10 boxes for a product flow without a spiderweb

Splitting is not automatically “cleaner.” Together is right when pieces share information, when joining them shrinks the interface, or when the same edit would otherwise be duplicated. Apart is right when one piece is general (retry, identity, clocks) and the other is special (this campaign’s copy, this screen’s layout).

A useful test: if you split A and B, does each interface get simpler, or did you just add a pass-through method and a pass-through variable? Pass-throughs mean the split was a fiction—the knowledge still travels through every layer.

Pull complexity downward. The lower module should absorb the ugly cases so callers stay calm. Configuration dumped on every caller is complexity pushed up. A text buffer that handles newline quirks so the editor does not is complexity pulled down.

Different layers should mean different abstractions. If layer N’s methods are the same names as layer N+1 with no extra meaning, you have a sandwich, not a design.

On a whiteboard, aim for 6–10 boxes, not 25. Each box is a deep module: a job, a small interface, a secret. Lines are dependencies you are willing to admit.

Onboarding: create account, verify email, pick a plan, first session.

Weak split: Step1Controller, Step2Controller, … plus a shared User bag that every step mutates. The steps are apart on the diagram and together in the worst way: they all know the whole record.

Better together: one OnboardingSession that owns “where is this person in the flow” and the invariants (“cannot pick a plan before verify”).

Better apart: Identity (who are they, are they verified) vs PlanCatalog (what can they buy) vs FirstSession (what we show after). Identity does not know about plan prices. FirstSession asks Identity and PlanCatalog questions; it does not store card numbers.

Push complexity down: retries, “email provider is slow,” and “user clicked the link twice” live in Identity, not in the screen that says “check your inbox.”

flowchart TB
  client[Client surfaces]
  session[OnboardingSession]
  identity[Identity]
  plans[PlanCatalog]
  first[FirstSession]
  client --> session
  session --> identity
  session --> plans
  session --> first

If you feel the urge to draw arrows from every screen to every store, you have not decided who owns the session.

Timer on. Candidate side only: you are explaining to someone who has not seen the product.

  1. Prompt: “Design in-app messaging for a consumer app (inbox, push, email).”
  2. Write requirements as questions you would ask, then assume answers out loud.
  3. Draw 6–10 boxes. Label each with a verb-ish job (route, render, consent, deliver).
  4. Pick one seam and say why those two boxes are apart (or together).
  5. Do not deep-dive yet. Stop at the shape.
  • A Philosophy of Software Design — Chapter 8, Pull Complexity Downwards
  • A Philosophy of Software Design — Chapter 9, Better Together Or Better Apart?
  • A Philosophy of Software Design — Chapter 7, Different Layer, Different Abstraction