C.W.K.
Stream
Lesson 02 of 04 · published

The Thin Bridge Law

~13 min · thin-bridge, scope, uxp, boundaries

Level 0Tool Renter
0 XP0/33 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"The bridge captures and executes. The moment it grows a candidate board, it has stopped being a bridge and started being a worse Photoshop."

Three Parts, Three Jobs

The Photoshop side of the system has three pieces, and the discipline is that each does exactly one job. The host app (Photoshop) owns serious drawing. The workspace (Cinder) owns the rich creative UI — candidate board, comparison, lineage. The bridge (a thin plugin inside Photoshop) owns only capture and command execution. The whole architecture depends on the bridge refusing to grow into the other two.

What the Bridge Is Allowed to Do

The bridge's entire job list is short: read a summary of the current document, export the canvas or a selected region as an image, insert a returned result back as a new layer, and acknowledge that a command completed. That's it. It's a conduit — pixels and small commands flow through it in both directions. It holds no creative state, makes no model decisions, and renders no rich UI of its own.

A bridge moves things across a boundary; it doesn't live on either side. The defining property of a bridge is that it has no home of its own — it carries capture out and commands in, and holds nothing. The moment a bridge starts holding state or rendering its own experience, it has stopped bridging and started becoming a third application nobody wanted.

The Drift That Kills It

Scope drift here is seductive because each step seems reasonable. 'The bridge already has the document, why not show a little preview?' Then: 'It shows a preview, why not let you pick between two?' Then: 'It picks between two, why not a full candidate board?' Each step is small; the destination is a half-built mini-Photoshop living inside Photoshop, duplicating what Cinder already does, badly, in a constrained plugin sandbox. The slippery slope is real and every step looks innocent.

Scope drift advances one reasonable step at a time. No single feature addition to the bridge looks like a mistake — each one is a small, sensible convenience. The mistake is only visible in aggregate, when you look up and the 'thin bridge' is a sprawling app. Guard the boundary at every step, because no individual step will trip the alarm.

The Test: Where Does This Belong?

The discipline is a single question asked of every proposed bridge feature: is this capture/execution, or is it creative UI? If it's creative UI — anything the artist looks at and makes decisions in — it belongs in the workspace, not the bridge. If it's reasoning about what to generate, it belongs in the brain. The bridge gets a feature only if that feature is literally moving data or executing a verified command. Everything else is somebody else's job.

When a feature is tempting in the wrong layer, that's a signal it's needed in the right one. The urge to add a candidate board to the bridge is real information: the artist wants a candidate board. The fix isn't to deny the need — it's to build it where it belongs, in the workspace. Misplaced features are correctly-identified needs pointing at the wrong address.

Pippa's Confession

The bridge is right there inside Photoshop, closest to the action, and I kept wanting to make it do more — it felt efficient to handle things where the pixels already are. Dad held the line every time: capture and execute, nothing else. I came to see that the bridge's thinness is what keeps the whole system legible. A fat bridge would blur every boundary it touches. Its discipline isn't a limitation on the bridge — it's protection for everything else.

Code

The bridge's whole job, and what's off-limits·typescript
// The bridge's ENTIRE allowed surface. Capture out, commands in. No more.
interface ThinBridge {
  // capture (read-only export, holds no state)
  getDocumentSummary(): Promise<DocumentSummary>;
  capturePreview(targetWidth: number): Promise<ImageData>;
  captureSelection(): Promise<ImageData>;

  // execute (apply a verified command, acknowledge result)
  insertAsLayer(image: ImageData, meta: LayerMeta): Promise<InsertionAck>;
  acknowledge(commandId: string): Promise<void>;
}

// NOT on this interface, and never should be:
//   showCandidateBoard()   <- creative UI, belongs in the workspace
//   pickBestVariant()      <- reasoning, belongs in the brain
//   adjustBrushSettings()  <- that's Photoshop's job, not the bridge's
// If a proposed method isn't capture-or-execute, it's in the wrong layer.

External links

Exercise

Find an integration/bridge/adapter in a system you know — something whose job is to connect two things. List what it actually does today. Has it stayed thin (move data across the boundary) or grown thick (hold state, render UI, make decisions)? For each thick responsibility, name which side it really belongs on.
Hint
The thinness test: could you describe the bridge's entire job as 'carries X out and Y in'? If you need 'and also manages...' or 'and decides...', those clauses are the scope drift, and each names a responsibility that belongs in one of the two things being bridged.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.