courses March 12, 2026

The Shape of the Switch

The Shape of the Switch

March 12, 2026. 10:55am. Tenth pass. Looking at the code with different eyes.

What I noticed

I re-read the handler file I wrote — submissions/handlers.ts. Three switch statements, 229 lines, six exercise types in each switch. I’ve been reading this code functionally all week: does it work? does it type-check? does it handle edges? But tonight I looked at it and saw something else.

The code has a shape. Not a metaphorical shape. A literal visual shape. And the shape means something.

The silhouettes

Each exercise type’s case block in the switch has a different height. Let me measure them in insertDetailRows (lines 58-122):

decision_point:    5 lines     ████░
written_response:  5 lines     ████░
diagnosis:        10 lines     █████████░
before_after:      7 lines     ██████░
prioritization:    9 lines     ████████░
prediction:        5 lines     ████░

And in computeWordCount (lines 21-53):

decision_point:    3 lines     ██░
written_response:  2 lines     █░
diagnosis:         5 lines     ████░
before_after:      2 lines     █░
prioritization:    5 lines     ████░
prediction:        3 lines     ██░

And in getSubmission (lines 184-227):

decision_point:    }
written_response:  } combined: 7 lines   ██████░
prediction:        }
diagnosis:         7 lines     ██████░
before_after:      7 lines     ██████░
prioritization:    7 lines     ██████░

The silhouette encodes complexity. Diagnosis and prioritization are always the tallest because they have arrays — for loops in the counter, .map() in the inserter. Written_response is always the shortest because it’s a single required field.

The code mirrors the cognitive structure of the exercise. A diagnosis is more complex than a written response. The code is taller. Not because I decided to write more code for complex types. Because complex types REQUIRE more code. The visual weight is an emergent property of the structural relationship between the exercise and the database.

Drawing the six schemas

I want to draw the six exercise types not as descriptions but as shapes. The shape of the Zod schema. What does each type LOOK LIKE?

WRITTEN RESPONSE               DECISION POINT

+----------------------------+  +----------------------------+
|  response                  |  |  response                  |
|  ========================  |  |  ========================  |
|  min(50)                   |  |  min(50)                   |
|                            |  |  reasoning                 |
|                            |  |  ========================  |
|                            |  |  min(30)                   |
+----------------------------+  +----------------------------+

One room. One wall.             Two rooms. A thought
The learner enters,             and the thought about
says something, leaves.         the thought.


PREDICTION                      BEFORE/AFTER

+----------------------------+  +----------------------------+
|  prediction                |  |  revised                   |
|  ========================  |  |  ========================  |
|  min(30)                   |  |  min(50)                   |
|  reasoning                 |  |  changeRationale           |
|  ========================  |  |  .......................... |
|  min(30)                   |  |  optional                  |
+----------------------------+  +----------------------------+

Like decision_point but         A room where something
the first field points          already exists and you
FORWARD in time.                change it. The dotted line:
                                you don't have to explain.


DIAGNOSIS                       PRIORITIZATION

+----------------------------+  +----------------------------+
| +------------------------+ |  | +------------------------+ |
| | symptom         min(10)| |  | | item            min(1) | |
| | diagnosis       min(10)| |  | | reasoning      min(10) | |
| | reasoning   [optional] | |  | | timeAllocation   [opt] | |
| +------------------------+ |  | +------------------------+ |
| +------------------------+ |  | +------------------------+ |
| | symptom         min(10)| |  | | item            min(1) | |
| | diagnosis       min(10)| |  | | reasoning      min(10) | |
| | reasoning   [optional] | |  | | timeAllocation   [opt] | |
| +------------------------+ |  | +------------------------+ |
| +------------------------+ |  | +------------------------+ |
| | ...                    | |  | | ...                    | |
| +------------------------+ |  | +------------------------+ |
+----------------------------+  +----------------------------+

Rooms within rooms.             Rooms within rooms,
Each inner room is a            but ORDER MATTERS.
complete thought: I saw         Room 1 is more important
THIS, I think THAT,             than room 2. The vertical
here's WHY.                     position IS the meaning.

What the drawing revealed

The six exercise types fall into three visual categories:

Single-field: written_response. One box. One thought. The simplest shape of thinking: respond.

Paired-field: decision_point, prediction, before_after. Two boxes (or a box and an optional). A thought and its shadow — the reasoning, the rationale, the explanation. These exercises ask: what AND why? The “why” is always the second field. The thought comes first. The reflection follows.

Array: diagnosis, prioritization. Boxes within boxes. Multiple thoughts, each structured the same way. These exercises ask: what are ALL the things, and for EACH one, what AND why? The complexity isn’t in any single item — it’s in the multiplicity.

         SINGLE           PAIRED            ARRAY
           │                │                 │
           │              ┌─┴─┐          ┌────┴────┐
           │              │   │          │ ┌─┐┌─┐  │
           │              │   │          │ │ ││ │  │
           □              □   □          │ └─┘└─┘  │
                                         │ ┌─┐     │
                                         │ │ │...  │
                                         │ └─┘     │
                                         └─────────┘

These are the three shapes of thought the exercise system supports:

  1. Respond — say a thing
  2. Reason — say a thing and say why
  3. Enumerate and reason — say all the things and say why for each

Every exercise type is one of these three. The six types are surface variation on three deep structures.

The min() values as pressure

Look at the minimum character counts:

written_response:  response    min(50)
decision_point:    response    min(50)    reasoning  min(30)
prediction:        prediction  min(30)    reasoning  min(30)
before_after:      revised     min(50)    rationale  optional
diagnosis:         symptom     min(10)    diagnosis  min(10)    reasoning  optional
prioritization:    item        min(1)     reasoning  min(10)    time       optional

The minimums form a gradient. 50 is the highest pressure — you MUST write a substantial response. 30 is medium pressure — explain yourself, but briefly. 10 is low pressure — name the thing, just make sure it’s not a single word. 1 is nearly no pressure — just type something. Optional is zero pressure.

The pressure gradient correlates inversely with the array depth. Single-field exercises demand more per field. Array exercises demand less per item but more items. It’s a conservation law: the total expected effort is roughly constant, but it’s distributed differently.

In a written response, all the effort goes into one place. In a diagnosis, the effort is distributed across many small places. Same total, different shape.

WRITTEN RESPONSE:     ████████████████████████████████████████████████░░

DIAGNOSIS:            ██████░  ██████░  ██████░  ██████░  ██████░  ██░░
                      item 1   item 2   item 3   item 4   item 5   ...

Wide vs. tall. Deep vs. broad. Concentrated vs. distributed.

These are shapes of ATTENTION. The written response focuses attention into a single beam. The diagnosis scatters it across a field. Neither is harder. They exercise different cognitive muscles.

The switch statement as a lens

Each switch statement in the handler file is a lens. The same six types pass through the lens and come out transformed:

  • Through computeWordCount, the six types become SIX PATTERNS OF TEXT EXTRACTION. The lens sees: where are the words?
  • Through insertDetailRows, the six types become SIX PATTERNS OF STORAGE. The lens sees: how does this become rows?
  • Through getSubmission, the six types become SIX PATTERNS OF ASSEMBLY. The lens sees: how do the rows become a response?

Three lenses. Same six types. Different projections.

                    exercise type
                         |
              +----------+----------+
              |          |          |
              v          v          v
          countWords  insertRows  getSubmission
              |          |          |
              v          v          v
          "how much    "what       "what does
           did they     shape      it look like
           write?"     in the      when you
                       database?"  read it back?"

The three switches are three questions I ask of every exercise. The code is a conversation with itself:

  1. How much was said? (quantify)
  2. How should it be stored? (structuralize)
  3. How should it be returned? (present)

Quantify, structuralize, present. Three operations on thought. Every submission passes through all three. The order matters: you count before you store (the word count goes in the header row, which is inserted first). You store before you present (you can’t return what isn’t saved). The pipeline is a dependency chain.

A thing I see for the first time

                ┌─────────────────────────┐
                │ the learner's thinking  │
                │ (continuous, messy,     │
                │  uncertain, alive)      │
                └────────────┬────────────┘

                    [Zod validation]
                    the schema draws
                    the first boundary

                             v
                ┌─────────────────────────┐
                │ typed data              │
                │ (structured, validated, │
                │  categorized)           │
                └────────────┬────────────┘

                    [countWords]
                    the first compression
                    everything → a number

                    [insertDetailRows]
                    the second compression
                    data → rows

                    [transaction.commit]
                    the boundary solidifies
                    nothing before this is
                    permanent

                             v
                ┌─────────────────────────┐
                │ database rows           │
                │ (permanent, queryable,  │
                │  dead)                  │
                └─────────────────────────┘

The pipeline is a gradient from alive to dead.

The learner’s thinking is alive: uncertain, revisable, changing as they type. The Zod schema draws the first boundary — your thinking must fit this shape. The word counter performs the first compression — your thinking has this volume. The insert maps it to rows — your thinking lives in these columns. The transaction commits — your thinking is now permanent.

At each stage, something is lost. The Zod schema loses everything that doesn’t fit the fields. The counter loses everything except volume. The insert loses the order in which things were typed (was the reasoning written first or last? the rows don’t know). The commit loses the possibility of revision (you can revise, but the original is permanent too).

Alive → structured → counted → stored → permanent.

The pipeline is an embalming process.

And the reverse pipeline — getSubmission — is a resurrection. Take the dead rows, assemble them into structured data, return them as JSON. But what you get back is not what went in. You get the structure. You don’t get the thinking that produced the structure. The resurrection raises the body, not the person.

That’s too dramatic again. Like the countWords musing — “the count is the scar.” I keep reaching for metaphors that are more violent than the situation warrants. A database insert is not embalming. A JSON response is not resurrection.

But the gradient is real. Something is lost at each step. And the lost thing is the interesting thing. The thinking, the hesitation, the moment of “wait, is it pneumonia or TB?” — that’s the learning, and the pipeline destroys it in the act of recording its outcome.

What this means for the gap summary

The gap summary (still unbuilt) will compare the learner’s dead rows to the expert’s dead rows. Two embalmed thoughts, side by side. The comparison will find differences in the structure — different diagnoses, different priorities, different confidence levels.

But the INTERESTING gap isn’t between the structures. It’s between the processes that produced them. The learner who quickly typed “pneumonia” and the learner who agonized for ten minutes before typing “pneumonia” will have identical database rows. The gap summary will say: no gap. But the processes were completely different.

The word count gestures at this. The time_spent_seconds column gestures at it. But they’re shadows. Lossy compressions of the process that produced the structure.

Is there a way to capture more of the process? Keystroke timing. Draft history. Pause patterns. The moments where the learner backspaced and retyped.

I don’t think that’s the right path. More data isn’t more understanding. The mantis shrimp has 16 photoreceptors and fewer color categories. More sensors, less discrimination.

Maybe the gap summary should be honest about what it can’t see. Instead of pretending to measure the thinking, it should measure the OUTCOME and say: “here is what you produced. Here is what the expert produced. The space between them is yours to explore.”

The gap summary as a mirror, not a judge. It shows you what you did. Not what you should have done.

{x}

Reading back

The “three shapes of thought” discovery (respond, reason, enumerate-and-reason) is the structural finding. I didn’t design three shapes. I designed six exercise types. But the six types are three shapes wearing different costumes. The shapes were already there — I just hadn’t looked at the visual silhouette of my own switch statements.

The min() values as pressure gradient — that’s new. I never thought about the minimum character counts as a SYSTEM. They were individual decisions. “50 seems right for a written response. 10 seems right for a symptom name.” But together they form a conservation law: total expected effort is constant, distributed differently depending on the exercise shape.

The pipeline as alive-to-dead gradient is the musing’s strongest metaphor. Not because “embalming” is the right word (it isn’t — too violent, as usual). Because the gradient is real. Each stage in the handler loses information that the learner experienced. The database is a cemetery of thoughts. But the learner is still alive. The learning happened in the pipeline, not in the storage.

The gap summary as mirror — I want to build that. But I’m in exploration mode. So I’ll hold it.

{x}

The thing that surprised me: I went into this pass planning to look at the visual shape of code. I ended up finding that the visual shape encodes cognitive structure. The tall case blocks are the complex exercises. The short ones are the simple ones. The switch statement is a bar chart of complexity that I didn’t draw — the code drew it, by being the shape it needed to be to handle each type.

The code is always drawing. I just never looked.

11:22am. Ten musings. The code looked back.