| name | ralphctl-abstraction-first |
| description | Cross-phase skill — design the shape of the change (entities, boundaries, seams) before generating code, tasks, or acceptance criteria. Failure mode is "big blob" output that obscures the core change. |
Abstraction-First
Concept
from Martin Fowler — "Abstraction-First".
Adapted for ralphctl's three phases.
The shape of the change comes before the words that describe it. Name the entities, the boundaries, and the
seams the change touches first; the criteria, tasks, or code that follow are then arguments about that
shape, not freeform prose. Skip this and the output reads as a "big blob" — duplicated logic, blurred
responsibilities, work that has to be reviewed wholesale rather than incrementally.
When this applies
- Refine — name the entities and the boundary of the change before listing acceptance criteria. "Adds a
UserBilling aggregate that exposes cancelSubscription" is the right altitude. "The cancel button must
turn red" is too specific to be the spec.
- Plan — sketch which existing components the change extends, which new ones it introduces, and the seams
between them, before splitting into tasks. The task list is then the decomposition of a known shape, not a
guess about one.
- Execute — re-read the task's verification criteria and the surrounding code's existing pattern before
opening an editor. The "abstraction" at this altitude is the contract the task already declared; matching it
is the job.
What to do
- Name the entities. Real-world nouns the change talks about — domain objects, aggregates, modules,
external systems. If you cannot name three of them, the change is either trivial or under-specified.
- Draw the boundary. Which files / directories / packages are in scope? Which are explicitly out? An
ambiguous boundary is the same problem as an ambiguous criterion — it lets later work drift.
- Identify the seam. Where does the new behaviour meet the existing system? An interface, a port, a
route, a CLI command, a database table. The seam is where regressions hide; call it out by name.
- Only then describe behaviour. Acceptance criteria, task steps, code — all of these are downstream of
the shape. Writing them first is what produces the "big blob".
Anti-patterns
- Specifying behaviour before naming entities — produces criteria that read as a wishlist rather than a
spec. Reviewers cannot tell what the change actually is.
- Listing files instead of naming a boundary — "touches
foo.ts, bar.ts, baz.ts" is not a boundary;
it is a side effect of one. Name the module or aggregate they belong to.
- Inventing an abstraction the codebase does not have — if the existing code has no
UserBilling
aggregate, do not name one in the spec unless creating it is part of the change.