@hobin/developer 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -20
- package/SOURCES.md +104 -0
- package/extensions/developer.ts +202 -44
- package/extensions/references/behavior-preserving-structural-change.md +149 -0
- package/extensions/skills.ts +1 -16
- package/extensions/state.ts +96 -10
- package/extensions/tui.ts +140 -25
- package/package.json +11 -25
- package/skills/abstraction-review/references/field-card.md +7 -0
- package/skills/abstraction-review/references/recipe-cards.md +67 -0
- package/skills/abstraction-review/references/repair-table.md +5 -0
- package/skills/model/SKILL.md +6 -1
- package/skills/model/references/problem-modeling.md +294 -235
- package/skills/model/references/worked-models-and-specialized-techniques.md +218 -0
- package/skills/naming-judgment/SKILL.md +5 -3
- package/skills/naming-judgment/references/domain-naming.md +202 -0
- package/skills/schedule/SKILL.md +1 -1
- package/skills/schedule/references/structural-change-timing.md +80 -13
- package/skills/signal/SKILL.md +2 -2
- package/skills/signal/references/structural-movement.md +202 -0
- package/skills/sketch/SKILL.md +35 -8
- package/skills/sketch/references/abstraction-barriers-and-closure.md +160 -0
- package/skills/sketch/references/abstraction-composition-and-state.md +184 -0
- package/skills/sketch/references/composition-generative-recursion-and-accumulators.md +196 -0
- package/skills/sketch/references/data-driven-design.md +177 -0
- package/skills/sketch/references/data-shape-template-catalog.md +241 -0
- package/skills/sketch/references/generic-operations-and-languages.md +134 -0
- package/skills/sketch/references/processes-state-and-time.md +171 -0
- package/skills/sketch/references/responsibility-and-variation.md +245 -0
- package/skills/verify/references/verifier-selection-and-pass-but-wrong.md +61 -3
- package/skills/naming-judgment/references/elements-of-clojure-naming.md +0 -74
- package/skills/signal/references/flocking-and-structural-movement.md +0 -157
- package/skills/sketch/references/design-recipe-and-abstraction-barriers.md +0 -169
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Generic Operations And Languages
|
|
2
|
+
|
|
3
|
+
Use this reference when several representations must coexist additively, or
|
|
4
|
+
when ordinary data and functions are becoming a small language with its own
|
|
5
|
+
evaluation semantics.
|
|
6
|
+
|
|
7
|
+
## Two Axes Of Generic Operations
|
|
8
|
+
|
|
9
|
+
Write the matrix before choosing classes, visitors, multimethods, or a registry:
|
|
10
|
+
|
|
11
|
+
| Representation | `charge` | `refund` | `describe` |
|
|
12
|
+
| --- | --- | --- | --- |
|
|
13
|
+
| Card | method | method | method |
|
|
14
|
+
| Bank transfer | method | unsupported | method |
|
|
15
|
+
| Store credit | method | method | method |
|
|
16
|
+
|
|
17
|
+
There are two independent pressures:
|
|
18
|
+
|
|
19
|
+
- horizontal: higher-level operations should not know representation fields;
|
|
20
|
+
- vertical: adding a representation should not require editing every old
|
|
21
|
+
representation package.
|
|
22
|
+
|
|
23
|
+
One possible data-directed surface is:
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
register(kind, operation, method)
|
|
27
|
+
applyGeneric(operation, taggedValue, ...args)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Specify duplicate registration, missing method, load order, type transitions,
|
|
31
|
+
and error visibility. Then add a fake representation. Existing callers and old
|
|
32
|
+
representation modules should remain unchanged. If only two stable variants
|
|
33
|
+
exist, a local conditional may be cheaper and clearer.
|
|
34
|
+
|
|
35
|
+
## Coercion And Meaning Preservation
|
|
36
|
+
|
|
37
|
+
For mixed representations, draw paths:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
Integer -> Rational -> Decimal
|
|
41
|
+
Money(USD) -X-> Money(EUR) without an exchange-rate context
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For each edge state preserved meaning and lost ability: precision, identity,
|
|
45
|
+
order, metadata, or capability. Choose direct operation, coercion, canonical
|
|
46
|
+
representation, or explicit rejection. A convenient path that silently loses
|
|
47
|
+
meaning is not genericity.
|
|
48
|
+
|
|
49
|
+
## Symbolic Data Boundary
|
|
50
|
+
|
|
51
|
+
When code inspects formulas, queries, or rules, represent notation as data:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
Expr = Number(value) | Variable(name) | Sum(left, right) | Product(left, right)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Algorithms use predicates/selectors/constructors or pattern matching on this
|
|
58
|
+
semantic definition, not string indexes. Constructors may own simplification:
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
makeSum(0, x) -> x
|
|
62
|
+
makeSum(x, 0) -> x
|
|
63
|
+
makeSum(x, y) -> Sum(x, y)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
That policy must be explicit because it changes the representation while
|
|
67
|
+
preserving intended expression meaning.
|
|
68
|
+
|
|
69
|
+
## When Data Becomes A Language
|
|
70
|
+
|
|
71
|
+
A rule table, query builder, workflow schema, or configuration is a language
|
|
72
|
+
only when it has:
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
primitives: smallest valid expressions
|
|
76
|
+
combination: how expressions form larger expressions
|
|
77
|
+
abstraction: how recurring expressions receive reusable names
|
|
78
|
+
validation: well-formed versus invalid programs
|
|
79
|
+
evaluation: what an expression means in an environment
|
|
80
|
+
errors: parse, validation, runtime, and unsupported behavior
|
|
81
|
+
evolution: versioning, migrations, and compatibility
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Worked policy DSL sketch:
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
Policy = Allow(Role) | HasTag(Tag) | All(List<Policy>) | Any(List<Policy>)
|
|
88
|
+
|
|
89
|
+
evaluate(Allow("admin"), env) = env.user.role == "admin"
|
|
90
|
+
evaluate(All([]), env) = true
|
|
91
|
+
evaluate(All([p, ...rest]), env) = evaluate(p, env) && evaluate(All(rest), env)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Define invalid states, such as an unknown role or empty `Any`, rather than
|
|
95
|
+
letting a host-language exception become language semantics.
|
|
96
|
+
|
|
97
|
+
## Evaluator Boundary
|
|
98
|
+
|
|
99
|
+
Keep the evaluator contract distinct from syntax:
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
evaluate : Program x Environment -> Result | LanguageError
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
State evaluation order, scope, effects, termination limits, and resource
|
|
106
|
+
budgets. A mathematically equivalent rewrite may not preserve host-language
|
|
107
|
+
short-circuiting, effects, or exceptions.
|
|
108
|
+
|
|
109
|
+
Do not build a DSL when ordinary typed data plus functions keep policy more
|
|
110
|
+
visible. The evaluator, migrations, debugging tools, and error messages are
|
|
111
|
+
product code, not free infrastructure.
|
|
112
|
+
|
|
113
|
+
## Artifact
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
Variant-operation matrix:
|
|
117
|
+
Representation barrier:
|
|
118
|
+
Registration/dispatch and unsupported policy:
|
|
119
|
+
Conversion graph and preserved meaning:
|
|
120
|
+
Language primitives/combinations/abstractions:
|
|
121
|
+
Validation and evaluator contract:
|
|
122
|
+
Errors, effects, versioning, and limits:
|
|
123
|
+
Fake variant or example program check:
|
|
124
|
+
Simpler ordinary-data alternative:
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Source Trace
|
|
128
|
+
|
|
129
|
+
- *Structure and Interpretation of Computer Programs*, Second Edition,
|
|
130
|
+
sections 2.4-2.5 and chapters 4-5: multiple representations, generic
|
|
131
|
+
operations, data-directed programming, symbolic data, metalinguistic
|
|
132
|
+
abstraction, evaluators, and explicit machines.
|
|
133
|
+
- Hillel Wayne, *Logic for Programmers*, version 0.14.0, May 4, 2026:
|
|
134
|
+
model/runtime distinctions and ability-guarantee tradeoffs.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Processes, State, And Time
|
|
2
|
+
|
|
3
|
+
Use this reference when returned values alone do not describe correctness. The
|
|
4
|
+
same procedure text can generate recursive, iterative, tree-shaped, delayed, or
|
|
5
|
+
stateful processes with different time, space, order, and failure behavior.
|
|
6
|
+
|
|
7
|
+
## Procedure Versus Process
|
|
8
|
+
|
|
9
|
+
Trace a representative execution:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
procedure: retry(operation, 3)
|
|
13
|
+
process:
|
|
14
|
+
attempt 1 -> retryable failure -> wait 100 ms
|
|
15
|
+
attempt 2 -> retryable failure -> wait 200 ms
|
|
16
|
+
attempt 3 -> success
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Record:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
result
|
|
23
|
+
work generated at each step
|
|
24
|
+
deferred work or stack growth
|
|
25
|
+
state retained or duplicated
|
|
26
|
+
time/space scale
|
|
27
|
+
ordering and failure surface
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
A recursive tree walk and an iterative queue walk may return the same nodes but
|
|
31
|
+
have different stack, ordering, and memory contracts. A fluent one-line pipeline
|
|
32
|
+
may materialize three full collections. Short procedure text is not evidence of
|
|
33
|
+
a cheap or simple process.
|
|
34
|
+
|
|
35
|
+
## State Means History Matters
|
|
36
|
+
|
|
37
|
+
Compare two calls with identical explicit input:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
account.withdraw(40) -> Accepted
|
|
41
|
+
account.withdraw(40) -> Rejected
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
If prior interactions explain the difference, state is part of the contract.
|
|
45
|
+
Fill:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
required history: accepted deposits and withdrawals
|
|
49
|
+
sufficient summary: current balance
|
|
50
|
+
owner: account instance
|
|
51
|
+
writers: deposit and withdraw
|
|
52
|
+
identity: two equal balances may be different accounts
|
|
53
|
+
aliases: references that observe the same mutation
|
|
54
|
+
order law: sufficient-funds check and balance update are one transition
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Local state is modular when it hides a coherent history behind a small
|
|
58
|
+
interface. It is hazardous when aliases, retries, concurrency, or persistence
|
|
59
|
+
make the history owner unclear.
|
|
60
|
+
|
|
61
|
+
## Explicit State Machine Alternative
|
|
62
|
+
|
|
63
|
+
Represent time as data when transitions need review, replay, or distribution:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
Payment = Created | Authorizing(attempt) | Authorized(id) | Failed(reason)
|
|
67
|
+
|
|
68
|
+
transition(Created, Authorize) -> Authorizing(1)
|
|
69
|
+
transition(Authorizing(n), Retryable) -> Authorizing(n + 1)
|
|
70
|
+
transition(Authorizing(_), Approved(id)) -> Authorized(id)
|
|
71
|
+
transition(Authorized(_), Approved(_)) -> invalid or idempotent policy
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
State the unchanged part of each transition and cover stale, duplicate,
|
|
75
|
+
reordered, retried, and concurrent events. A correct enum is not a correct
|
|
76
|
+
history model.
|
|
77
|
+
|
|
78
|
+
## Event Order And Atomicity
|
|
79
|
+
|
|
80
|
+
For concurrent withdrawal, the forbidden interleaving is:
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
A reads 100
|
|
84
|
+
B reads 100
|
|
85
|
+
A writes 20 after withdrawing 80
|
|
86
|
+
B writes 20 after withdrawing 80
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Both calls appear accepted although 160 was withdrawn. The order law is not
|
|
90
|
+
“serialize everything”; it is:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
read balance -> check funds -> write next balance
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
must behave as one protected transition for a given account. Choose a lock,
|
|
97
|
+
transaction, queue, compare-and-swap loop, or merge policy only after stating
|
|
98
|
+
that law and its waiting/fairness cost.
|
|
99
|
+
|
|
100
|
+
## Streams And Logs
|
|
101
|
+
|
|
102
|
+
An explicit stream/log represents successive states or events:
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
events -> fold(transition, initialState) -> currentState
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
It improves replay, audit, time travel, merge reasoning, and functional
|
|
109
|
+
composition. It also creates retention, ordering, identity, versioning, and
|
|
110
|
+
reconstruction-cost obligations. Choose it when history is product evidence,
|
|
111
|
+
not merely because mutation is unfashionable.
|
|
112
|
+
|
|
113
|
+
For delayed streams also record demand:
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
producer pace
|
|
117
|
+
consumer pace
|
|
118
|
+
buffer/backpressure
|
|
119
|
+
single-use versus replayable
|
|
120
|
+
failure and cancellation
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Pull, Transform, Push Process Check
|
|
124
|
+
|
|
125
|
+
```text
|
|
126
|
+
pull: where external time and failure enter
|
|
127
|
+
transform: domain computation and its data contract
|
|
128
|
+
push: effect interpretation, idempotency, and partial failure
|
|
129
|
+
apex: coordinator that owns the whole process outcome
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The separation is false when pull retries silently alter transform semantics or
|
|
133
|
+
push performs an unreported partial success. Conversely, splitting one atomic
|
|
134
|
+
transaction into these phases may violate the actual invariant.
|
|
135
|
+
|
|
136
|
+
## Process/State Artifact
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
Procedure promise:
|
|
140
|
+
Representative process trace:
|
|
141
|
+
Shape and time/space cost:
|
|
142
|
+
Required history and sufficient summary:
|
|
143
|
+
State or log owner and writers:
|
|
144
|
+
Identity and alias behavior:
|
|
145
|
+
Order/atomicity law:
|
|
146
|
+
Retry, stale, duplicate, and concurrency policy:
|
|
147
|
+
Alternative representation considered:
|
|
148
|
+
Evidence and residual risk:
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Failure Diagnosis
|
|
152
|
+
|
|
153
|
+
- Result tests pass but stack, buffering, or order matters: add a process-shaped
|
|
154
|
+
verifier.
|
|
155
|
+
- Same call changes but no history is named: revise the state contract.
|
|
156
|
+
- A state machine lists states but not events and next states: derive the
|
|
157
|
+
transition relation.
|
|
158
|
+
- All work is serialized: narrow the protected order law.
|
|
159
|
+
- A log is proposed without replay/audit/merge pressure: local state may be the
|
|
160
|
+
smaller honest model.
|
|
161
|
+
- A pipeline stage hides effects: expose the phase or rename the process
|
|
162
|
+
honestly.
|
|
163
|
+
|
|
164
|
+
## Source Trace
|
|
165
|
+
|
|
166
|
+
- *Structure and Interpretation of Computer Programs*, Second Edition,
|
|
167
|
+
chapters 1 and 3: procedure/process distinction, state and assignment,
|
|
168
|
+
identity and sharing, concurrency, constraints, delayed evaluation, and
|
|
169
|
+
streams.
|
|
170
|
+
- Zachary Tellman, *Elements of Clojure*, 2019: units of computation and the
|
|
171
|
+
construction of pull-transform-push processes.
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# Responsibility And Variation
|
|
2
|
+
|
|
3
|
+
Use this reference when a real change exposes misplaced knowledge, repeated
|
|
4
|
+
arguments, data clumps, conditional dispatch, type transitions, object creation,
|
|
5
|
+
or uncertainty about which collaboration should absorb variation.
|
|
6
|
+
|
|
7
|
+
## Begin With The Change
|
|
8
|
+
|
|
9
|
+
Do not design toward classes, polymorphism, or a factory in advance. State the
|
|
10
|
+
accepted change and show where the current code resists it. A smell identifies a
|
|
11
|
+
point of attack, not the final architecture.
|
|
12
|
+
|
|
13
|
+
Keep a simple concrete solution when it remains understandable and cheap to
|
|
14
|
+
change. A more elaborate design is justified by localized future change, not by
|
|
15
|
+
lower line count or the prestige of a pattern.
|
|
16
|
+
|
|
17
|
+
## Inventory Responsibilities
|
|
18
|
+
|
|
19
|
+
Describe what the current unit:
|
|
20
|
+
|
|
21
|
+
- knows;
|
|
22
|
+
- calculates or decides;
|
|
23
|
+
- coordinates;
|
|
24
|
+
- creates;
|
|
25
|
+
- sends to collaborators;
|
|
26
|
+
- changes for independent reasons.
|
|
27
|
+
|
|
28
|
+
Write wishful pseudocode in messages rather than field manipulation. A message
|
|
29
|
+
asks a receiver that owns relevant knowledge to do work. Repeatedly pulling data
|
|
30
|
+
out of one object so another can decide what it means is evidence of misplaced
|
|
31
|
+
responsibility.
|
|
32
|
+
|
|
33
|
+
## Smells As Design Evidence
|
|
34
|
+
|
|
35
|
+
Inspect smells on the accepted change path:
|
|
36
|
+
|
|
37
|
+
- a value repeatedly passed through methods that already belong to one concept;
|
|
38
|
+
- several fields or parameters that move as a data clump;
|
|
39
|
+
- conditionals that select behavior by a stable domain variant;
|
|
40
|
+
- methods that mostly inspect another unit's data;
|
|
41
|
+
- a coordinator that both chooses a variant and implements each variant;
|
|
42
|
+
- object construction mixed into domain collaboration;
|
|
43
|
+
- a chain of navigation that exposes collaborator structure.
|
|
44
|
+
|
|
45
|
+
The sketch should show how one concrete movement would make the change local.
|
|
46
|
+
Do not clean unrelated smells to make the design look complete.
|
|
47
|
+
|
|
48
|
+
## Extracting A Responsibility
|
|
49
|
+
|
|
50
|
+
A candidate unit is credible when data and behavior form a coherent concept with
|
|
51
|
+
its own reason to change. State:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
Responsibility: what the unit owns
|
|
55
|
+
Knowledge: data or history required to fulfill it
|
|
56
|
+
Messages: minimal caller-visible protocol
|
|
57
|
+
Hidden detail: mechanics callers stop knowing
|
|
58
|
+
Creation: who constructs or selects it
|
|
59
|
+
Representative change: change that becomes local
|
|
60
|
+
Rejected placement: why the old owner was wrong
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Move callers gradually when old and new forms can coexist. The implementation
|
|
64
|
+
protocol owns those edits; this reference only shapes the target collaboration.
|
|
65
|
+
|
|
66
|
+
Immutability often makes an extracted value easier to share and reason about,
|
|
67
|
+
but it is not mandatory when identity or local history is the product model.
|
|
68
|
+
|
|
69
|
+
## Variation And Substitution
|
|
70
|
+
|
|
71
|
+
Use polymorphism only when variants share a real role and callers can send the
|
|
72
|
+
same message without knowing the concrete type. State the role contract and
|
|
73
|
+
unsupported cases.
|
|
74
|
+
|
|
75
|
+
A subtype or implementation must preserve caller expectations. A special case
|
|
76
|
+
that raises, changes return meaning, or requires stronger preconditions may not
|
|
77
|
+
belong under the common role. Model old and new contracts before claiming safe
|
|
78
|
+
substitution.
|
|
79
|
+
|
|
80
|
+
Conditionals are not categorically wrong. Keep them when the variation is local,
|
|
81
|
+
finite, readable, and unlikely to grow independently. Replace them when new
|
|
82
|
+
accepted variants repeatedly force old behavior owners to change.
|
|
83
|
+
|
|
84
|
+
## Type Transitions
|
|
85
|
+
|
|
86
|
+
When one domain value leads to another kind of value, model the transition as a
|
|
87
|
+
domain relation rather than scattered numeric or string manipulation. State:
|
|
88
|
+
|
|
89
|
+
- current type and next type;
|
|
90
|
+
- operation or event causing the transition;
|
|
91
|
+
- preserved meaning;
|
|
92
|
+
- boundary or terminal behavior;
|
|
93
|
+
- whether the target selects itself or a collaborator decides.
|
|
94
|
+
|
|
95
|
+
Transitions that violate a common role may require a narrower protocol or an
|
|
96
|
+
explicit result type instead of inheritance.
|
|
97
|
+
|
|
98
|
+
## Object Creation And Factories
|
|
99
|
+
|
|
100
|
+
Creation policy belongs at a boundary where variant information is available.
|
|
101
|
+
Keep domain collaborators dependent on the role they use, not on class-name or
|
|
102
|
+
registration mechanics.
|
|
103
|
+
|
|
104
|
+
Factory designs form a continuum: a concrete conditional, configurable mapping,
|
|
105
|
+
registration, self-registration, or automatic discovery each moves knowledge
|
|
106
|
+
and adds operational cost. Compare:
|
|
107
|
+
|
|
108
|
+
- who knows available variants;
|
|
109
|
+
- how a new variant is introduced;
|
|
110
|
+
- unsupported and duplicate registration behavior;
|
|
111
|
+
- load order, reflection, naming, and deployment assumptions;
|
|
112
|
+
- testability and failure visibility.
|
|
113
|
+
|
|
114
|
+
Choose the least powerful mechanism that satisfies real extension pressure.
|
|
115
|
+
Pushing construction to the edge often removes the need for an elaborate
|
|
116
|
+
factory inside the domain.
|
|
117
|
+
|
|
118
|
+
## Dependency Direction And Navigation
|
|
119
|
+
|
|
120
|
+
High-level product behavior should depend on stable roles rather than concrete
|
|
121
|
+
variant mechanics. Inject a dependency when it represents a caller-chosen
|
|
122
|
+
collaborator, not merely to make every object configurable.
|
|
123
|
+
|
|
124
|
+
Long navigation chains reveal structural knowledge. Prefer asking the first
|
|
125
|
+
meaningful collaborator for the domain result, but do not add forwarding methods
|
|
126
|
+
that only hide an unchanged data path. The receiving responsibility must become
|
|
127
|
+
more coherent.
|
|
128
|
+
|
|
129
|
+
## Tests As Role Evidence
|
|
130
|
+
|
|
131
|
+
Choose units to test by public responsibility and cost of failure, not by one
|
|
132
|
+
test file per class. Use integration evidence for collaborations and focused
|
|
133
|
+
unit evidence for stable role contracts or difficult cases.
|
|
134
|
+
|
|
135
|
+
Fakes are useful when they express the same role without importing irrelevant
|
|
136
|
+
context. Tests that echo implementation structure, duplicate the same behavior
|
|
137
|
+
at every layer, or preserve obsolete context inhibit redesign.
|
|
138
|
+
|
|
139
|
+
## Complete Example: Move Discount Knowledge
|
|
140
|
+
|
|
141
|
+
Accepted change: subscriptions now support a partner discount in addition to a
|
|
142
|
+
student discount. Current `Checkout` receives `student`, `partner`, `country`,
|
|
143
|
+
and `subtotal`, then selects and calculates every discount itself.
|
|
144
|
+
|
|
145
|
+
### Inventory the current owner
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
Checkout knows:
|
|
149
|
+
customer flags, discount precedence, formulas, country exclusions
|
|
150
|
+
Checkout decides:
|
|
151
|
+
which discount applies
|
|
152
|
+
Checkout coordinates:
|
|
153
|
+
pricing, payment, receipt
|
|
154
|
+
Checkout changes when:
|
|
155
|
+
discount policy, payment flow, or receipt flow changes
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The problem is not the conditional alone. Discount knowledge and behavior have
|
|
159
|
+
a different reason to change from checkout coordination.
|
|
160
|
+
|
|
161
|
+
### Write wished messages
|
|
162
|
+
|
|
163
|
+
```text
|
|
164
|
+
discount = discountPolicy.for(customer, country)
|
|
165
|
+
payable = discount.applyTo(subtotal)
|
|
166
|
+
checkout.charge(payable)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Candidate responsibility:
|
|
170
|
+
|
|
171
|
+
```text
|
|
172
|
+
Responsibility: choose and apply the accepted discount policy
|
|
173
|
+
Knowledge: eligibility, precedence, exclusions, calculation
|
|
174
|
+
Messages: for(customer, country); applyTo(money)
|
|
175
|
+
Hidden detail: flags and formula selection
|
|
176
|
+
Creation: composition root supplies current policy set
|
|
177
|
+
Representative change: add partner eligibility and formula locally
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
If student and partner discounts share `applyTo(Money) -> Money` with the same
|
|
181
|
+
preconditions and result meaning, they may honor one role. If partner discount
|
|
182
|
+
requires an asynchronous provider lookup, it cannot silently substitute for a
|
|
183
|
+
pure discount. The boundary may instead return an eligibility decision or move
|
|
184
|
+
the lookup to the pull phase.
|
|
185
|
+
|
|
186
|
+
A local finite rule table may remain clearer than a class per discount. Add a
|
|
187
|
+
factory or registry only if creation or extension pressure is real. Constructing
|
|
188
|
+
the policy set at the edge keeps `Checkout` independent without inventing
|
|
189
|
+
discovery.
|
|
190
|
+
|
|
191
|
+
Smallest implementation surface:
|
|
192
|
+
|
|
193
|
+
```text
|
|
194
|
+
1. characterize current student behavior with responsibility-level examples
|
|
195
|
+
2. introduce DiscountPolicy behind the existing result
|
|
196
|
+
3. move one policy calculation while keeping Checkout behavior green
|
|
197
|
+
4. add partner behavior through the same caller-visible contract
|
|
198
|
+
5. inspect whether the role remains coherent; stop at the stable landing
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
This reference shapes the collaboration. The direct execution protocol owns the
|
|
202
|
+
sender-by-sender mutation, and `abstraction-review` decides whether the candidate
|
|
203
|
+
is stable enough to keep.
|
|
204
|
+
|
|
205
|
+
## Sketch Output
|
|
206
|
+
|
|
207
|
+
```text
|
|
208
|
+
Change pressure: accepted change exposing the design issue
|
|
209
|
+
Current responsibility inventory: knows, decides, coordinates, creates
|
|
210
|
+
Point of attack: concrete smell on the change path
|
|
211
|
+
Wished messages: collaboration in domain language
|
|
212
|
+
Candidate ownership: data, behavior, hidden detail, reason to change
|
|
213
|
+
Variation model: role, variants, substitution and unsupported cases
|
|
214
|
+
Creation boundary: selection knowledge and factory tradeoff
|
|
215
|
+
Checks: behavior, role, integration, and pass-but-wrong cases
|
|
216
|
+
First item: smallest implementation step
|
|
217
|
+
Deferred: patterns or variants not yet supported by evidence
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Failure Checks
|
|
221
|
+
|
|
222
|
+
- a pattern or class hierarchy is selected before the change is stated;
|
|
223
|
+
- responsibility is inferred from duplicated syntax alone;
|
|
224
|
+
- an extracted object has no knowledge or reason to change of its own;
|
|
225
|
+
- polymorphism hides incompatible contracts;
|
|
226
|
+
- a factory solves hypothetical discovery or registration needs;
|
|
227
|
+
- dependency injection adds configuration without isolating real variation;
|
|
228
|
+
- navigation is hidden by forwarding methods without moving responsibility;
|
|
229
|
+
- tests freeze current internals rather than verify roles and behavior.
|
|
230
|
+
|
|
231
|
+
## Source Trace
|
|
232
|
+
|
|
233
|
+
- Sandi Metz, Katrina Owen, and TJ Stankus, *99 Bottles of OOP*, Second
|
|
234
|
+
Edition, version 2.2.2, 2024: chapters 3-9 on listening to change, smells and
|
|
235
|
+
points of attack, responsibility extraction, argument removal, immutability,
|
|
236
|
+
Liskov substitution, conditional-to-polymorphic movement, type transitions,
|
|
237
|
+
factories, wishful pseudocode, dependency inversion, Law of Demeter, creation
|
|
238
|
+
at the edge, and responsibility-focused testing.
|
|
239
|
+
- Harold Abelson and Gerald Jay Sussman with Julie Sussman, *Structure and
|
|
240
|
+
Interpretation of Computer Programs*, Second Edition, MIT Press, 1996:
|
|
241
|
+
multiple representations, generic operations, data-directed programming, and
|
|
242
|
+
additivity.
|
|
243
|
+
- Zachary Tellman, *Elements of Clojure*, 2019: indirection cost, module
|
|
244
|
+
assumptions, and the distinction between cohesive components and adaptable
|
|
245
|
+
interfaces.
|
|
@@ -11,10 +11,11 @@ addition to visible behavior.
|
|
|
11
11
|
- Verifier Ladder
|
|
12
12
|
- Execution Versus Relevance
|
|
13
13
|
- Pass-But-Wrong Search
|
|
14
|
+
- Test Design And Cost
|
|
14
15
|
- Structural Degradation
|
|
15
16
|
- Feedback Classification
|
|
16
17
|
- Residual Risk And Stop Checks
|
|
17
|
-
-
|
|
18
|
+
- Complete Evidence Example
|
|
18
19
|
|
|
19
20
|
## Claim Before Check
|
|
20
21
|
|
|
@@ -104,6 +105,31 @@ current checks while violating the intended meaning. Look for:
|
|
|
104
105
|
When a plausible shape remains cheap to test, add the smallest distinguishing
|
|
105
106
|
fixture. Otherwise record it as residual risk instead of broadening the claim.
|
|
106
107
|
|
|
108
|
+
## Test Design And Cost
|
|
109
|
+
|
|
110
|
+
Tests are design evidence only when their boundary and maintenance cost remain
|
|
111
|
+
useful. Prefer tests that expose public responsibility and product meaning over
|
|
112
|
+
tests that echo private method structure.
|
|
113
|
+
|
|
114
|
+
Choose the unit by the claim:
|
|
115
|
+
|
|
116
|
+
- focused examples for stable responsibility and boundary cases;
|
|
117
|
+
- role or contract tests when several implementations must substitute;
|
|
118
|
+
- integration tests for collaborations, creation, persistence, and wiring;
|
|
119
|
+
- properties when many examples share a generatable rule;
|
|
120
|
+
- stateful fixtures when history, retry, order, or identity matters.
|
|
121
|
+
|
|
122
|
+
Do not require one test layer per class. Loose coupling may make a smaller unit
|
|
123
|
+
economical to test, while simple forwarding or framework glue may be better
|
|
124
|
+
covered by a larger behavior check. Fakes should remove irrelevant context while
|
|
125
|
+
preserving the collaborator role; mocks that restate call internals create an
|
|
126
|
+
echo chamber.
|
|
127
|
+
|
|
128
|
+
Delete or reorganize tests only when the remaining evidence still supports the
|
|
129
|
+
same claims. Redundancy is sometimes cheap insurance and sometimes obsolete
|
|
130
|
+
context that prevents a design from moving. State which claim each retained test
|
|
131
|
+
protects.
|
|
132
|
+
|
|
107
133
|
## Structural Degradation
|
|
108
134
|
|
|
109
135
|
Behavior may pass while the change weakens the code's ability to preserve the
|
|
@@ -153,7 +179,7 @@ Verification is complete enough when:
|
|
|
153
179
|
Do not claim total correctness from a finite evidence set. State the strongest
|
|
154
180
|
claim justified now.
|
|
155
181
|
|
|
156
|
-
##
|
|
182
|
+
## Complete Evidence Example
|
|
157
183
|
|
|
158
184
|
```text
|
|
159
185
|
Claim: recurring schedules without an end date remain valid.
|
|
@@ -166,4 +192,36 @@ Residual: timezone behavior remains unverified and is not included in the
|
|
|
166
192
|
supported claim.
|
|
167
193
|
```
|
|
168
194
|
|
|
169
|
-
|
|
195
|
+
Now attempt a wrong implementation that always emits `recurring-open` whenever
|
|
196
|
+
`endDate` is absent. It passes the open-recurring row but also misclassifies a
|
|
197
|
+
one-time schedule whose UI omitted an optional display field. Add a one-time row
|
|
198
|
+
with the same absence shape but a different recurrence discriminator. This
|
|
199
|
+
fixture fails the plausible wrong rule while remaining at the public conversion
|
|
200
|
+
boundary.
|
|
201
|
+
|
|
202
|
+
Map the broader completion claim:
|
|
203
|
+
|
|
204
|
+
| Claim | Verifier | What it can still miss |
|
|
205
|
+
| --- | --- | --- |
|
|
206
|
+
| semantic variants convert correctly | focused table test | persisted legacy shapes not in the table |
|
|
207
|
+
| public type accepts intended callers | typecheck plus real caller build | runtime external input |
|
|
208
|
+
| legacy saved schedules remain readable | migration fixture through persistence adapter | unknown production corruption |
|
|
209
|
+
| UI shows the converted meaning | interaction test or manual run | timezone/environment variance |
|
|
210
|
+
| package contains the changed source | packed-tarball inspection and fresh install | registry propagation delay |
|
|
211
|
+
|
|
212
|
+
If only the first row is executed, report only the first claim. The correct
|
|
213
|
+
result is a narrower supported statement, not an unqualified “done.”
|
|
214
|
+
|
|
215
|
+
## Source Trace
|
|
216
|
+
|
|
217
|
+
- Hillel Wayne, *Logic for Programmers*, version 0.14.0, May 4, 2026:
|
|
218
|
+
strong and weak tests, partial specifications, structural properties,
|
|
219
|
+
property-based testing, contracts, and proof limitations.
|
|
220
|
+
- Sandi Metz, Katrina Owen, and TJ Stankus, *99 Bottles of OOP*, Second
|
|
221
|
+
Edition, version 2.2.2, 2024: chapters 2 and 9 on cost-effective tests,
|
|
222
|
+
avoiding implementation echo, choosing units, unit versus integration tests,
|
|
223
|
+
context independence, fakes, role verification, and obsolete-test removal.
|
|
224
|
+
- Matthias Felleisen, Robert Bruce Findler, Matthew Flatt, and Shriram
|
|
225
|
+
Krishnamurthi, *How to Design Programs, Second Edition*, MIT Press, 2018:
|
|
226
|
+
representative examples and tests as successive constraints in a design
|
|
227
|
+
recipe.
|