- name
- breadboarding
- description
- Transform a workflow description into affordance tables showing UI and Code affordances with their wiring. Use to map existing systems or design new ones from shaped parts.
# Breadboarding
Breadboarding transforms a workflow description into a complete map of affordances and their relationships. The output is always a set of tables showing numbered UI and Code affordances with their Wires Out and Returns To relationships. The tables are the truth. Mermaid diagrams are optional visualizations for humans.
---
## Use Cases
Breadboarding serves two functions:
### 1. Mapping an Existing System
You don't understand how an existing system works in its concrete details. You have a workflow you're trying to understand — explaining how something happens or why something doesn't happen.
**Input:**
- Code repo(s) to analyze
- Workflow description (always from the perspective of an operator trying to make an effect happen — through UI or as a caller)
**Output:**
- UI Affordances table
- Code Affordances table
- (Optional) Mermaid visualization
**Note:** If the workflow spans multiple applications (frontend + backend), create ONE breadboard that tells the full story. Label places to show which system they belong to.
### 2. Designing from Shaped Parts
You have a new system sketched as an assembly of parts (mechanisms) per shaping. You need to detail out the concrete mechanism and show how those parts interact as a system.
**Input:**
- Parts list (mechanisms from shaping)
- The R (requirement/outcome) the parts are meant to achieve
- Existing system (optional) — if the new parts must interoperate with existing code
**Output:**
- UI Affordances table
- Code Affordances table
- (Optional) Mermaid visualization
### Mixtures
Often you have both: an existing system that must remain as-is, plus new pieces or changes defined in a shape. In this case, breadboard both together — the existing affordances and the new ones — showing how they connect.
### 3. Reading a Whiteboard Breadboard
Hand-drawn or whiteboard breadboards use a visual stacking format rather than tables. The same concepts apply (Places, affordances, wiring) but the layout conventions differ.
**Visual conventions:**
| Element | How it appears |
|---------|---------------|
| **Place** | Colored block (often pink/purple) at the **top** of a vertical stack |
| **Affordances in a place** | Blocks stacked **underneath** the place block — containment is shown by vertical position in the stack |
| **Code affordances** | Typically float **between** place stacks, not inside them |
| **Place loader** | A code affordance positioned at the **top-left** of the place block — describes the data/inputs needed to render that place |
| **Wires Out** | Solid arrows between blocks |
| **Returns To** | Dashed arrows between blocks |
| **Conditionals** | Indented blocks within a stack, often a different color (e.g., green), showing if/else branches |
| **Place references** | `_` prefix on a place name within a stack (same as `_PlaceName` convention) |
| **Uncertain/tentative** | `?` prefix or `~` prefix on an affordance name, or dashed borders — indicates the affordance is speculative |
| **Containing box** | A large boundary drawn around multiple stacks — groups affordances by system or responsibility boundary (e.g., "HireEZ" box) |
| **Notes/annotations** | Freeform text near elements — context, open questions, or rationale |
**How to read a whiteboard breadboard:**
1. **Identify places** — Find the colored header blocks at the top of each stack
2. **Read each stack top-to-bottom** — Everything stacked under a place belongs to that place
3. **Find loaders** — Code affordances at the top-left of a place block describe what data is needed to render
4. **Trace wiring** — Follow arrows between stacks for control flow (solid) and data flow (dashed)
5. **Note conditionals** — Indented blocks with different colors show branching logic within a place
6. **Check containing boxes** — Large boundaries indicate system/responsibility boundaries
7. **Flag speculative items** — `?` and `~` prefixed items are uncertain and may not survive shaping
**Translating to tables:** When converting a whiteboard breadboard to standard affordance tables, map each stack to its Place, enumerate the affordances top-to-bottom, and capture the arrows as Wires Out / Returns To relationships. Loaders become code affordances with Returns To pointing at the UI affordances they feed.
---
## Core Concepts
### Places
A Place is a **bounded context of interaction**. While you're in a Place:
- You have a specific set of affordances available to you
- You **cannot** interact with affordances outside that boundary
- You must take an action to leave
**Place is perceptual, not technical.** It's not about URLs or components — it's about what the user experiences as their current context. A Place is "where you are" in terms of what you can do right now.
#### The Blocking Test
The simplest test for whether something is a different Place: **Can you interact with what's behind?**
| Answer | Meaning |
|--------|---------|
| **No** | You're in a different Place |
| **Yes** | Same Place, with local state changes |
#### Examples
| UI Element | Blocking? | Place? | Why |
|------------|-----------|--------|-----|
| Modal | Yes | Yes | Can't interact with page behind |
| Confirmation popover | Yes | Yes | Must respond before returning (limit case of modal) |
| Edit mode (whole screen transforms) | Yes | Yes | All affordances changed |
| Checkbox reveals extra fields | No | No | Surroundings unchanged |
| Dropdown menu | No | No | Can click away, non-blocking |
| Tooltip | No | No | Informational, non-blocking |
#### Local State vs Place Navigation
When a control changes state, ask: did *everything* change, or just a subset while the surroundings stayed the same?
| Type | What happens | How to model |
|------|--------------|--------------|
| **Local state** | Subset of UI changes, surroundings unchanged | Same Place, conditional N → dependent Us |
| **Place navigation** | Entire screen transforms, or blocking overlay | Different Places |
#### Mode-Based Places
When a mode (like "edit mode") transforms the entire screen — different buttons, different affordances everywhere — model as separate Places:
```
PLACE: CMS Page (Read Mode)
PLACE: CMS Page (Edit Mode)
```
The state flag (e.g., `editMode$`) that switches between them is a **navigation mechanism**, not a data store. Don't include it as an S in either Place.
#### Three Questions for Any Control
For any UI affordance, ask:
1. Where did I come from to see this?
2. Where am I now?
3. Where do I go if I act on it?
If the answer to #3 is "everything changes" or "I can't interact with what's behind until I respond," that's navigation to a different Place.
#### Labeling Conventions
| Pattern | Use |
|---------|-----|
| `PLACE: Page Name` | Standard page/route |
| `PLACE: Page Name (Mode)` | Mode-based variant of a page |
| `PLACE: Modal Name` | Modal dialog |
| `PLACE: Backend` | API/database boundary |
When spanning multiple systems, label with the system: `PLACE: Checkout Page (frontend)`, `PLACE: Payment API (backend)`.
### Place IDs
Places are first-class elements in the data model. Each Place gets an ID:
| # | Place | Description |
|---|-------|-------------|
| P1 | CMS Page (Read Mode) | View-only state |
| P2 | CMS Page (Edit Mode) | Editing state with page-level controls |
| P2.1 | widget-grid (letters) | Subplace: letter editing widget within P2 |
| P3 | Letter Form Modal | Form for adding/editing letters |
| P4 | Backend | API resolvers and database |
Place IDs enable:
- **Explicit navigation wiring** — wire `→ P2` instead of to an affordance inside
- **Containment tracking** — each affordance declares which Place it belongs to
- **Consistent Mermaid subgraphs** — subgraph ID matches Place ID
### Place References
When a nested place has lots of internal affordances and would clutter the parent, you can **detach** it:
1. Put a **reference node** in the parent place using underscore prefix: `_letter-browser`
2. Define the full place separately with all its internals
3. Wire from the reference to the place: `_letter-browser --> letter-browser`
The reference is a **UI affordance** — it represents "this widget/component renders here" in the parent context.
```mermaid
flowchart TB
subgraph P1["P1: CMS Page (Read Mode)"]
U1["U1: Edit button"]
U_LB["_letter-browser"]
end
subgraph letterBrowser["letter-browser"]
U10["U10: Search input"]
U11["U11: Letter list"]
N40["N40: performSearch()"]
end
U_LB --> letterBrowser
```
In affordance tables, list the reference as a UI affordance:
| # | Affordance | Control | Wires Out |
|---|------------|---------|-----------|
| U1 | Edit button | click | → N1 |
| _letter-browser | Widget reference | — | → P3 |
Style place references with a dashed border to distinguish them:
```
classDef placeRef fill:#ffb6c1,stroke:#d87093,stroke-width:2px,stroke-dasharray:5 5
class U_LB placeRef
```
### Modes as Places
When a component has distinct modes (read vs edit, viewing vs editing, collapsed vs expanded), model them as **separate places** — they're different perceptual states for the user.
If one mode includes everything from another plus more, show this with a **place reference** inside the extended place:
```
P3: letter-browser (Read) — base state
P4: letter-browser (Edit) — contains _letter-browser (Read) + new affordances
```
The reference shows composition: "everything in P3 appears here, plus these additions."
```mermaid
flowchart TB
subgraph P3["P3: letter-browser (Read)"]
U10["U10: Search input"]
U11["U11: Letter list"]
end
subgraph P4["P4: letter-browser (Edit)"]
U_P3["_letter-browser (Read)"]
U3["U3: Add button"]
U4["U4: Edit button"]
end
U_P3 --> P3
```
In affordance tables for P4, the reference shows inheritance:
| # | Affordance | Control | Wires Out | Notes |
|---|------------|---------|-----------|-------|
| _letter-browser (Read) | Inherits all of P3 | — | → P3 | |
| U3 | Add button | click | → N3 | NEW |
| U4 | Edit button | click | → N4 | NEW |
### Subplaces
A **subplace** is a defined subset of a Place — a contained area that groups related affordances. Use subplaces when:
- A Place contains multiple distinct widgets or sections
- You're detailing one part of a larger Place
- You want to show what's in scope vs out of scope
**Notation:** Use hierarchical IDs — `P2.1`, `P2.2`, etc. for subplaces of P2.
```
| # | Place | Description |
|---|-------|-------------|
| P2 | Dashboard | Main dashboard page |
| P2.1 | Sales widget | Subplace: sales metrics |
| P2.2 | Activity feed | Subplace: recent activity |
```
In affordance tables, use the subplace ID to show containment:
```
| U3 | P2.1 | sales-widget | "Refresh" button | click | → N4 | — |
| U7 | P2.2 | activity-feed | activity list | render | — | — |
```
**In Mermaid:** Nest the subplace subgraph inside the parent. Use the same background color (no distinct fill) — the subplace is part of the parent, not a separate Place:
```mermaid
flowchart TB
subgraph P2["P2: Dashboard"]
subgraph P2_1["P2.1: Sales widget"]
U3["U3: Refresh button"]
end
subgraph P2_2["P2.2: Activity feed"]
U7["U7: activity list"]
end
otherContent[["... other dashboard content ..."]]
end
```
**Placeholder for out-of-scope content:** When detailing one subplace, add a placeholder sibling to show there's more on the page:
```
otherContent[["... other page content ..."]]
```
This tells readers: "we're zooming in on P2.1, but P2 contains more that we're not detailing."
### Containment vs Wiring
These are two different relationships in the data model:
| Relationship | Meaning | Where Captured |
|--------------|---------|----------------|
| **Containment** | Affordance belongs to / lives in a Place | **Place column** (set membership) |
| **Wiring** | Affordance triggers / calls something | **Wires Out column** (control flow) |
**Containment** is set membership: `U1 ∈ P1` means U1 is a member of Place P1. Every affordance belongs to exactly one Place.
**Wiring** is control flow: `U1 → N1` means U1 triggers N1. An affordance can wire to anything — other affordances or Places.
The Place column answers: "Where does this affordance live?"
The Wires Out column answers: "What does this affordance trigger?"
### Navigation Wiring
When an affordance causes navigation (user "goes" somewhere), wire to the **Place itself**, not to an affordance inside:
```
✅ N1 Wires Out: → P2 (navigate to Edit Mode)
❌ N1 Wires Out: → U3 (wiring to affordance inside P2)
```
This makes navigation explicit in the tables. The Place is the destination; specific affordances inside become available once you arrive.
In Mermaid, this becomes:
```
N1 --> P2
```
The subgraph ID matches the Place ID, so the wire connects to the Place boundary.
### Affordances
Things you can act upon:
- **UI affordances (U)**: inputs, buttons, displayed elements, scroll regions
- **Code affordances (N)**: methods, subscriptions, data stores, framework mechanisms
### Wiring
How affordances connect to each other:
**Wires Out** — What an affordance triggers or calls (control flow):
- Call wires: one affordance calls another
- Write wires: code writes to a data store
- Navigation wires: routing to a different place
**Returns To** — Where an affordance's output flows (data flow):
- Return wires: function returns value to its caller
- Read wires: data store is read by another affordance
This separation makes data flow explicit. Wires Out show control flow (what triggers what). Returns To show data flow (where output goes).
---
## The Output: Affordance Tables
The tables are the truth. Every breadboard produces these:
### Places Table
| # | Place | Description |
|---|-------|-------------|
| P1 | Search Page | Main search interface |
| P2 | Detail Page | Individual result view |
### UI Affordances Table
| # | Place | Component | Affordance | Control | Wires Out | Returns To |
Ver en GitHub