HackerTube 中文?
Software boundaries and change →

Does splitting code actually split its dependencies?

Start with a rule that sends an email. Separate deciding from doing, then inspect layers, objects and abstractions: which parts must still change together when the rule changes?

Questions in this reading · 11
  1. Where should a business rule end?
  2. Where do side effects belong?
  3. Can you isolate side effects in the language you already use?
  4. What should cross a module boundary?
  5. Do separate layers really separate concerns?
  6. Can small components still be tightly coupled?
  7. Is removing duplication worth another abstraction?
  8. What makes an object useful?
  9. When is it time to stop coding and rethink the problem?
  10. After splitting two classes, what proves they separated?
  11. Who chooses the options a person gets to see?

Where should a business rule end?

Separating independent concerns becomes concrete in a functional core: compute the decision from values, then let another layer carry out its effects.

Compare original passages and timestamps · 2
start seeing interconnections between things that could be independent
"Simple Made Easy" - Rich Hickey (2011) · ▶ 59:28–59:32
an imperative shell that surrounds the functional core.
Ruby Conf 12 - Boundaries by Gary Bernhardt · ▶ 14:48–14:54

Where do side effects belong?

Overwatch concentrates side effects on one side of an interaction. A functional core similarly leaves database access and email to its surrounding shell.

Compare original passages and timestamps · 2
all major behavioral side effects are localized to that call side
Overwatch Gameplay Architecture and Netcode · ▶ 18:20–18:26
an imperative shell that surrounds the functional core.
Ruby Conf 12 - Boundaries by Gary Bernhardt · ▶ 14:48–14:54

Can you isolate side effects in the language you already use?

Separate a decision from the code that reads a database or sends mail. The boundary depends on how data flows, and can be introduced within an existing language.

Compare original passages and timestamps · 2
no language features are required to program this style
Why Isn't Functional Programming the Norm? – Richard Feldman · ▶ 36:07–36:18
the expired users class makes all the decisions and the sweeper class has all the dependencies.
Ruby Conf 12 - Boundaries by Gary Bernhardt · ▶ 15:28–15:38

What should cross a module boundary?

Values have meanings that callers can inspect independently. A boundary that passes values makes decisions easier to exercise without setting up the surrounding infrastructure.

Compare original passages and timestamps · 2
values are immutable right and values are semantically transparent
The Value of Values with Rich Hickey · ▶ 10:43–10:54
we now have values as boundaries.
Ruby Conf 12 - Boundaries by Gary Bernhardt · ▶ 10:17–10:24

Do separate layers really separate concerns?

Placing code in different layers leaves the underlying concepts free to depend on each other. Trace which changes still require coordinated edits.

Compare original passages and timestamps · 2
it's a mistake to put optionality in aggregate definitions
Maybe Not - Rich Hickey · ▶ 30:36–31:48
don't associate simplicity with partitioning and stratification, right?
"Simple Made Easy" - Rich Hickey (2011) · ▶ 34:50–35:20

Can small components still be tightly coupled?

Changing component size and changing how components communicate are separate decisions. Passing a value can remove a dependency on a synchronous call.

Compare original passages and timestamps · 2
make smaller things
RailsConf 2014 - All the Little Things by Sandi Metz · ▶ 0:54–0:58
we now have values as boundaries.
Ruby Conf 12 - Boundaries by Gary Bernhardt · ▶ 10:17–10:24

Is removing duplication worth another abstraction?

Metz weighs the cost of a wrong abstraction against repeated code. Ousterhout asks how much capability a module hides behind a simple interface.

Compare original passages and timestamps · 2
It is far cheaper to keep duplication than it is to have to mess with the wrong abstraction.
RailsConf 2014 - All the Little Things by Sandi Metz · ▶ 14:52–14:59
very simple interface with a very large amount of functionality underneath it
A Philosophy of Software Design | John Ousterhout | Talks at Google · ▶ 14:24–14:29

What makes an object useful?

Compare refactoring a pricing rule into small objects with the cost of coordinating objects that carry mutable state. A class boundary alone says little about how its users interact.

Compare original passages and timestamps · 2
make smaller things
RailsConf 2014 - All the Little Things by Sandi Metz · ▶ 0:54–0:58
the problem is really not classes per se
Object-Oriented Programming is Bad · ▶ 1:37–1:42

When is it time to stop coding and rethink the problem?

Look for concerns that have become intertwined. Stepping away to reconsider the problem can reveal which of those dependencies really belong together.

Compare original passages and timestamps · 2
start seeing interconnections between things that could be independent
"Simple Made Easy" - Rich Hickey (2011) · ▶ 59:28–59:32
most of the big problems we have with software are problems of misconception
Hammock Driven Development - Rich Hickey · ▶ 4:27–4:36

After splitting two classes, what proves they separated?

Start with the obstacle, as in the design talk: changing the mail service also changes expiry calculations. A familiar library may feel easier, but Simplicity Matters distinguishes familiarity from coupled responsibilities. Sierra assembles declared dependencies outside the component; a transducer separates per-input processing from its collection or channel. Try two substitutions: change the service without editing the rule, then run that rule over a different input stream. Which still forces a rewrite?

Passages and timestamps · 4
describing, diagnosing, delimiting the problem, choosing a direction at a high level
"Design in Practice" by Rich Hickey · ▶ 21:30–22:46 · passage interval
It is really about the interleaving, not the cardinality.
Rails Conf 2012 Keynote: Simplicity Matters by Rich Hickey · ▶ 2:22–3:34 · passage interval
I'm going to do is declare my dependencies and they'll get filled in later
Stuart Sierra - Components Just Enough Structure · ▶ 17:23–17:40
the exact same process bags I defined here, this concrete thing being reused in a completely different context
"Transducers" by Rich Hickey · ▶ 11:23–14:43 · passage interval

Who chooses the options a person gets to see?

Urmson’s vehicle can slow down when a pedestrian might step out, buying time for another observation. Suarez describes a different decision boundary: when sensor output exceeds human review capacity, software selects what a reviewer sees. Final approval operates only on items admitted to the queue. Draw three steps—obtaining evidence, selecting candidates and authorizing action—and identify who can change what becomes visible at the next step.

Passages and timestamps · 2
Whereas a self-driving system can look at that pedestrian and say, I don't know what they're about to do, slow down, take a better look, and then react appropriately after that.
Chris Urmson: How a driverless car sees the road · ▶ 7:30–7:39
drones will tell humans what to look at, not the other way around.
Daniel Suarez: The kill decision shouldn't belong to a robot · ▶ 4:28–4:33

Try it on your own example

Trace one rule change before deciding where the module boundary belongs.