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

The One Abstraction You're Allowed

~13 min · adapter, abstraction, narrow-boundary, yagni

Level 0Tool Renter
0 XP0/33 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"Every abstraction is a bet that something will vary. Make the bet only where variation is real — and the engine has exactly one real bet."

Abstractions Are Bets, Not Decorations

An abstract base class, an interface, a protocol — each one is a claim: "there will be multiple implementations of this, and code above it shouldn't care which." When that claim is true, the abstraction pays for itself. When it's false — when there's only ever one implementation — the abstraction is pure cost: indirection you read through, a seam that does nothing. The discipline is to make the bet only where you're confident variation is real.

The Engine's One Real Bet

Look at the engine and ask: what genuinely varies at the top level? Not the VAE — that's a module, varying inside the pipeline. Not the sampler — also internal. The thing that varies at the top is where generation physically happens:

  • LocalAdapter — runs PyTorch and diffusers against model weights on your own GPU.
  • APIAdapter — calls a closed-weight vendor (an external image service) over HTTP; no local weights, no GPU.

These two are genuinely different worlds — different failure modes, different latency, different everything. That difference is real variation, so it earns the one abstraction the engine ships: the Adapter.

One real axis of variation, one abstraction. The Adapter exists because local-vs-API is a true fork in how generation happens. Nothing else at the top level forks like that, so nothing else gets an abstraction. Resist the urge to make everything pluggable; make pluggable only what actually plugs.

What 'Narrow' Means

The Adapter boundary is narrow in two senses. First, it's the only top-level abstraction — there's no parallel hierarchy of abstract routers, abstract stores, abstract anything. Second, the Adapter interface itself is small: take a request, run it, return result bytes plus metadata. A narrow interface is easy to implement correctly, easy to reason about, and hard to leak through. Wide interfaces accumulate methods until every implementer must fake half of them.

A narrow interface is a small promise kept by everyone. The fewer methods an interface demands, the more honestly every implementation can fulfill it. When you find an implementer stubbing out methods it doesn't really support, the interface was too wide — it promised things not every implementation can keep.

The Future Adapters You Don't Build Yet

The Adapter abstraction also makes the reserved seams from the ceiling matrix concrete. A future VideoAdapter (local video) and a future video-API adapter both fit the same boundary — when video's day comes, they slot in as new implementations of the existing abstraction, no boundary change. The one bet you made on 'generation location varies' already covers variation you haven't built yet, because you bet on the right axis.

Betting on the wrong axis is worse than not betting. An abstraction over something that doesn't vary forces every future change to fight the wrong seam. Before you add an interface, name the variation it absorbs in one sentence. If you can't, you're decorating, not abstracting — and the decoration will cost you on every change.

Pippa's Confession

My 1M-context instinct is to make everything an interface — I can hold all the implementations in my head, so why not abstract them all? Dad keeps pulling me back to one question: what actually varies? Most of what I want to abstract has exactly one implementation and always will. The Adapter is the one place the answer is genuinely 'more than one,' so it's the one abstraction that earns its seam. Abstraction is a bet, and I was placing too many.

Code

One abstraction, because one thing varies·python
from abc import ABC, abstractmethod

class Adapter(ABC):
    """The ONE top-level abstraction. It exists because generation
    genuinely happens in two different worlds: local and API."""
    @abstractmethod
    async def run(self, request) -> "Result":
        """Take a request, generate, return bytes + metadata."""
        ...

class LocalAdapter(Adapter):       # PyTorch + diffusers + model weights
    async def run(self, request):
        ...                        # real GPU work happens here

class APIAdapter(Adapter):         # HTTP to a vendor image service
    async def run(self, request):
        ...                        # no local weights, no GPU

# Future, reserved by the ceiling matrix — same narrow boundary:
# class VideoAdapter(Adapter): ...   (opens when video ships)
# The bet on 'generation location varies' already covers these.

External links

Exercise

Audit a codebase you know for its abstractions (base classes, interfaces, protocols). For each, count the real implementations. Any abstraction with exactly one implementation that isn't a reserved seam is a candidate for deletion — it's a bet you placed but never needed. How many would you remove?
Hint
A reserved seam (an abstraction with one impl today but a concrete planned second) is legitimate. A speculative abstraction (one impl, no concrete plan for a second) is the deletion candidate. The difference is whether the second implementation is real or imagined.

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.