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

One Seam, Not Ten

~11 min · narrow-seam, rule-2, adapter-pattern, architecture

Level 0Unlit
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"Abstract the one thing that varies. Leave everything else gloriously concrete."

The Inherited Rule

Firekeeper doesn't invent its architecture from scratch — it inherits a rule from the Pippa house: put the abstraction boundary in exactly one narrow place, and let everything specialize downstream of it. In the brain, that seam is the streaming-model API; above it, the whole app is written concretely, assuming one shape. In Firekeeper the same rule applies to two seams: the STT engine and the cleanup brain. Everywhere else, the code is direct and readable — no interfaces for the sake of interfaces.

Why Not Abstract Everything?

The tempting mistake is to make the whole app "provider-neutral" — abstract the audio layer, the insertion layer, the settings layer, all of it, just in case. That trades a real cost for an imaginary benefit. Every abstraction is a layer of indirection someone has to read through. You pay that tax in every file, forever, to enable a swap that happens in one place. The narrow-seam rule inverts it: pay the abstraction cost exactly where variation actually lives, and nowhere else.

Broad abstraction (avoid)        Narrow seam (do this)
------------------------         ---------------------
every layer is an interface      only STT + cleanup are interfaces
swap-ready everywhere            swap-ready where swaps happen
indirection tax in every file    concrete, readable everywhere else
flexibility you'll never use     flexibility exactly where you need it

Cost Flows Downstream

The deeper principle: when a new variant needs something (a new STT engine, a new cleanup brain), it absorbs the cost of fitting the existing seam — the seam does not grow new abstractions to accommodate it. Concrete-first, specialize-on-demand. You never generalize in anticipation of a variant that might arrive; you specialize when it actually does. That's how the Pippa family stays small at the core while supporting many surfaces, and Firekeeper is one of those surfaces following the same discipline.

Put the seam where the variation is. Identify the one or two things that genuinely have alternatives — here, which engine hears you and which brain cleans up — and abstract exactly those. Everything with only one real implementation stays concrete. Abstraction is a cost you pay to buy swappability; only buy it where you'll actually swap.
Concrete-first beats abstract-first. Write the concrete version, and extract an interface only when a second real implementation forces it. An interface invented before its second implementation almost always guesses the wrong shape, and you refactor it anyway when reality shows up. Let the second case design the seam.

Code

Two narrow seams; everything else concrete·swift
// The ONLY two abstractions in the pipeline — the things that vary.
protocol STTEngine { func finalize(_ c: AudioClip, language: LanguageMode) async throws -> STTResult }
protocol CleanupProvider { func clean(_ req: CleanupRequest) async throws -> CleanupResult }

// Everything else is a concrete type. No interface, no indirection,
// because there is only ever one implementation of each.
struct AudioCaptureService { /* ... */ }
struct TargetCaptureService { /* ... */ }
struct PasteboardInsertionService { /* ... */ }

// The controller names concrete services and two `any` seams. That's it.
// You can read it top to bottom without chasing abstractions.

External links

Exercise

Look at a codebase you know and count its interfaces/protocols. For each one, ask: does this have two or more real implementations, or was it added 'just in case'? How many could be deleted and replaced with the concrete type with zero loss today?
Hint
The 'just in case' interfaces are the ones with exactly one implementation and a vague intention of maybe swapping someday. Each is an indirection tax paid up front for a swap that may never come. The narrow-seam discipline says: delete it now, and extract the interface the day a real second implementation actually forces the shape.

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.