Learning path

Hierarchies in Backend Design

Mission: MISSION.md · Sources: RESOURCES.md · Calibrated 2026-08-18 · 5 lessons available

Progress

Ticks read from the same localStorage key the lessons write, so completing a lesson greens its nodes below. If the graph disagrees with what you did in a lesson tab, tick it here — browsers sometimes scope localStorage per file:// document.

mastered / completed taught, not yet completed outer fringe — ready, no lesson yet locked

Amber turns green as you mark lessons complete — the graph is live, not a snapshot. Click a node to open its lesson.

flowchart TD T1["Type vs value hierarchy
fixed-depth vs recursive"] T2["Adjacency list"] T3["Materialised path / ltree"] T4["Read-vs-write asymmetry"] T5["Recursive CTE mechanics"] T6["Nested set: why it lost"] T7["Closure table + transitive closure"] T8["The 4x5 operation matrix
the decision frame"] T9["Single source of truth
vs derived data"] T10["N+1 over a tree"] A1["Aggregate = consistency boundary
invariant, not relatedness"] A2["Small aggregates & OCC contention"] A3["Reference by identity"] A4["Client-generated identity
4 bug classes"] A5["Deferrable constraints"] A6["Cascade down, never up"] A7["Bounded subtree read"] C1["Standard vs custom methods
changes rows the caller didn't name"] C2["Nest or flatten the URL
finite route template test"] C3["UUIDv7 vs v4
index locality vs leakage"] C4["Idempotency key vs client IDs"] C5["parentId is not a PATCH field"] D1["Concurrent-move write skew
lock scope = invariant scope"] D2["Closure-table move in 2 statements"] D3["Sibling order: fractional indexing"] D4["Move = removal + insertion
two authorisation checks"] D5["Desired state over deltas"] E1["Dual-write problem"] E2["Transactional outbox + relay"] E3["At-least-once + inbox dedup"] E4["Per-aggregate ordering
+ orphan tolerance"] E5["Compensating action
& reconciliation"] N4["Permission inheritance
over a tree"] N5["DAGs / multi-parent
when a tree is a lie"] N6["Trash, restore, and
undo of a subtree delete"] T1 --> T8 T2 --> T5 T2 --> T8 T3 --> T8 T4 --> T8 T2 --> T10 T6 --> T8 T7 --> T8 T8 --> T9 T5 --> T9 T1 --> A1 A1 --> A2 A1 --> A3 A3 --> A4 A4 --> A5 A1 --> A6 T8 --> A7 T10 --> A7 A1 --> C1 T1 --> C2 A4 --> C3 A4 --> C4 C1 --> C5 C5 --> D1 T7 --> D2 D1 --> D4 T4 --> D3 C4 --> D5 A6 --> E1 C1 --> E1 E1 --> E2 T9 --> E2 E2 --> E3 E3 --> E4 A4 --> E4 A2 --> E5 E4 --> E5 T7 --> N4 A1 --> N4 T8 --> N5 C2 --> N5 A6 --> N6 E5 --> N6 classDef mastered fill:#d8ecd8,stroke:#5a7a5a,color:#1a1a18 classDef inner fill:#fde9b8,stroke:#a8862a,color:#1a1a18 classDef outer fill:#e2eafc,stroke:#4a6ea8,color:#1a1a18 classDef locked fill:#f2f0ea,stroke:#a09a8a,stroke-dasharray:4 3,color:#5d574a class T2,T3,T4 mastered class T1,T5,T6,T7,T8,T9,T10,A1,A2,A3,A4,A5,A6,A7,C1,C2,C3,C4,C5,D1,D2,D3,D4,D5,E1,E2,E3,E4,E5 inner class N4,N5,N6 outer click T1 "./lessons/a1f3-four-ways-to-store-a-tree.html" click T5 "./lessons/a1f3-four-ways-to-store-a-tree.html" click T6 "./lessons/a1f3-four-ways-to-store-a-tree.html" click T7 "./lessons/a1f3-four-ways-to-store-a-tree.html" click T8 "./lessons/a1f3-four-ways-to-store-a-tree.html" click T9 "./lessons/a1f3-four-ways-to-store-a-tree.html" click T10 "./lessons/a1f3-four-ways-to-store-a-tree.html" click A1 "./lessons/b7c2-aggregate-boundaries-in-hierarchies.html" click A2 "./lessons/b7c2-aggregate-boundaries-in-hierarchies.html" click A3 "./lessons/b7c2-aggregate-boundaries-in-hierarchies.html" click A4 "./lessons/b7c2-aggregate-boundaries-in-hierarchies.html" click A5 "./lessons/b7c2-aggregate-boundaries-in-hierarchies.html" click A6 "./lessons/b7c2-aggregate-boundaries-in-hierarchies.html" click A7 "./lessons/b7c2-aggregate-boundaries-in-hierarchies.html" click C1 "./lessons/c4e9-rest-commands-and-identity.html" click C2 "./lessons/c4e9-rest-commands-and-identity.html" click C3 "./lessons/c4e9-rest-commands-and-identity.html" click C4 "./lessons/c4e9-rest-commands-and-identity.html" click C5 "./lessons/c4e9-rest-commands-and-identity.html" click D1 "./lessons/d8b1-the-move-endpoint.html" click D2 "./lessons/d8b1-the-move-endpoint.html" click D3 "./lessons/d8b1-the-move-endpoint.html" click D4 "./lessons/d8b1-the-move-endpoint.html" click D5 "./lessons/d8b1-the-move-endpoint.html" click E1 "./lessons/e5f7-outbox-and-eventual-consistency.html" click E2 "./lessons/e5f7-outbox-and-eventual-consistency.html" click E3 "./lessons/e5f7-outbox-and-eventual-consistency.html" click E4 "./lessons/e5f7-outbox-and-eventual-consistency.html" click E5 "./lessons/e5f7-outbox-and-eventual-consistency.html"

Lessons

#LessonCovers
1 Four ways to store a tree 4×5 operation matrix; recursive CTE; nested set & closure table; type vs value hierarchy as the outranking question; single source of truth vs derived data; tree N+1
2 Where the transaction ends Aggregate as consistency boundary; contention from oversized aggregates; client-generated identity and its four bug classes; deferrable constraints; cascade policy; bounded subtree reads
3 REST, commands, and who invents identity Standard vs custom methods; nest-or-flatten the URL; UUIDv7 vs v4 with the counter-argument; idempotency key vs client IDs; why parentId must not be a PATCH field
4 The move endpoint Concurrent-move write skew and lock scope; the closure-table move; fractional indexing for sibling order; two authorisation checks; desired state over deltas
5 Eventual consistency between aggregates Dual-write problem; transactional outbox and relay; at-least-once with inbox dedup; per-aggregate ordering and orphan tolerance; compensating actions and reconciliation

Calibration record — 2026-08-18

ProbeResultAddressed
Subtree query on parent_id; name the model Gap. Names the model but delegates the query to an ORM — no recursive-CTE mental model, so no way to reason about read cost. Implies exposure to the tree N+1 trap.L1 §2, L1 Ex 2
Move cost: materialised path vs adjacency list Solid. 50k vs 1, with the right read/write inference unprompted.
Aggregate boundary for campaign/ad set/ad Right answer, wrong reason. Chose three, justified by a validation gate — a state machine, not an invariant.L2 §1
Dirty state / relaxed referential integrity Gap. No articulated bug classes.L2 §3, L5 §4
ON DELETE CASCADE as product semantics Gap (self-reported).L2 §4

Feedback incorporated

Teaching preferences

Next iteration — the outer fringe

  1. Permission inheritance over a tree (N4) — the closure table earns its keep here; this is where "who can see this folder" meets 50k descendants. Was out of scope in the mission; now the natural next step.
  2. DAGs and multi-parent (N5) — what to do when the tree is a lie: tags, shared folders, symlinks, org charts with dotted-line reports.
  3. Trash, restore, undo (N6) — the gap flagged in RESOURCES; L2 §4 promised soft delete but never covered restore semantics or orphan reparenting.

Say which one, or ask for a calibration round first if you'd rather I re-probe before writing.