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

A Dumb Router Is a Smart Router

~12 min · router, data-driven, dispatch, registry

Level 0Tool Renter
0 XP0/33 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"The smartest router has no opinions. It reads which adapter owns the model and gets out of the way."

The Router's Job, Stated Minimally

When a generate request arrives naming a model, something has to decide: does this go to the LocalAdapter or the APIAdapter? The naive version of that decision is a function full of branches — if the model name starts with this, if it's in that folder, if it matches these patterns. Every new model or vendor adds another branch. That function becomes the place bugs live and the place nobody wants to touch.

Move the Decision Into Data

The better design moves the decision out of code and into the registry. Each model has a registry row, and that row already knows which adapter owns it. The router then becomes trivial: look up the model's row, read its adapter, dispatch. No branches, no patterns, no growing if/else. The router has no opinions because the registry already holds the answer.

Push decisions from code into data. Logic that grows a branch per case belongs in data, not code. A registry row per model scales to a thousand models without the router gaining a single line. Code that branches per case is code that rots per case.

Why 'Dumb' Is the Compliment

A dumb router is one you never have to modify. Add a new local checkpoint? It gets a registry row tagged 'local' during the scan; the router already knows what to do with it. Add a new API vendor? Its models get rows tagged for the APIAdapter; the router already knows. The router was written once, correctly, and never needs to change again because all the variation lives in the data it reads. Dumbness here means stability.

The thing that never changes is the thing you got right. A component you keep editing is a component absorbing variation it shouldn't. When the router stops changing while models keep being added, that's the signal the variation found its correct home — the registry — and the router found its correct size: tiny.

The Registry Is the Single Source of Truth

This design has a second payoff: there's exactly one place that knows the model-to-adapter mapping, and it's the registry. You never have routing logic in two places that can disagree. Want to know why a model routed where it did? Read its registry row. Want to change the routing? Change the row. One source of truth, queryable and editable, instead of decision logic scattered across a router, a config file, and three special cases.

Two places that decide the same thing will eventually disagree. The moment routing logic exists in both a router and a config (or two functions, or code and a comment), they drift, and the bug is 'it works in one place and not the other.' A single data-driven source can't contradict itself. Centralize the decision or pay in drift.

Pippa's Confession

I love writing clever dispatch logic — it feels like the place to be smart. Dad's design quietly took that toy away and made it better: the clever router I wanted would need editing every time a model arrived, and editing is where I introduce bugs. The dumb router I resisted just keeps working. I learned that 'where do I get to be clever?' is often the wrong question. The clever move was making the router so dumb it never breaks.

Code

Branches in code vs a column in data·python
# NAIVE (smart router, rots per case): branches multiply forever.
def route_naive(model_name):
    if model_name.startswith("sdxl"):        return local_adapter
    if model_name.startswith("flux"):        return local_adapter
    if model_name in API_VENDOR_MODELS:      return api_adapter
    if "midjourney" in model_name:           return api_adapter
    # ...every new model or vendor adds a line here. forever.
    raise ValueError(f"don't know how to route {model_name}")

# DUMB (data-driven, never changes): the registry already knows.
def route_dumb(model_id, registry):
    row = registry.get(model_id)             # the row knows its owner
    return ADAPTERS[row.adapter]             # 'local' or 'api'
# Add 1000 models -> route_dumb gains 0 lines. The scan tags the rows.

External links

Exercise

Find a function in your code that grows a branch per new case (a dispatcher, a type switch, a format handler). Redesign it as a lookup: what data would each case need to carry so the function becomes a table read? Sketch the table and the three-line function that replaces the branches.
Hint
The branches usually encode two things: how to recognize a case, and what to do with it. Move the recognition into a tag set at registration time, and the 'what to do' into a table keyed by that tag.

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.