~13 min · cross-app-bug, vocabulary, integration, measurement
Level 0Loose Parts
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
One Name, Two Spellings
The assistant that every sibling app talks to can run on several different model brains. Two parts of the system needed to name them, and they were written at different times by different concerns.
The settings side published the list a user picks from, and one of its values was the vendor's consumer brand name. The chat-routing side owned the endpoints, and it knew that same brain by the name of its command-line tool. Both spellings were correct inside their own half. Neither half was wrong. There was simply no place where the two halves met and had to agree.
So every sibling app had the same defect: pick that brain in Settings, and the assistant panel stops working. Not one app. Every app, at once, for the same reason, discovered independently — which is the specific shape that tells you the defect is not in any app.
Why No Test Could Have Failed
This is the part worth sitting with. Each repository was internally consistent. The settings code stored what the settings endpoint published; the routing code routed what the routing table declared. A unit test in either half passes. An integration test inside one app passes too, as long as it uses a brain whose two spellings happen to coincide — and most of them did.
The defect lived in the space between two components that no single test suite contains. Duplication did not cause the naming split; the split would have been possible in a single application. What duplication did was multiply the blast radius and divide the ownership. Three apps had the bug. Zero apps had the responsibility for the boundary where it lived.
A fix has three homes; a divergence has none. That asymmetry is the whole argument. Duplicated code is annoying because a change must be applied N times, and everybody already knows that. The severe version is the inverse: when two copies disagree, no repository owns the disagreement, so no repository's tests can see it, and no repository's maintainer is at fault. Extraction is not primarily about typing less. It is about creating somewhere for the gap to live.
The Fix Names the Rule
The repair was not to rename either spelling. Both namespaces are legitimate: the picker shows what the settings owner publishes, and the routes accept what the route family declares. The fix was to give the boundary an owner — one translation function, in the shared layer, applied at exactly the moment a settings value crosses into a chat route, and never in reverse.
That is a much smaller change than "unify the vocabulary", and it is more honest. Two namespaces that exist for real reasons should be allowed to exist. What must not be allowed is for the crossing between them to be implicit, unwritten, and re-implemented in each caller. Track 5 comes back to this, because the same system later grew a second canonicalization running in the opposite direction, and the reason both must exist is the sharpest example in this quest of what shared code is really for.
Code
The shape of the split, and the one-line boundary that closes it·python
# --- What each half legitimately believed -------------------------
# The settings owner publishes the PICKER vocabulary. Users see brand
# names; this list is also what gets persisted in device settings.
PICKER_BRAINS = ("claude", "chatgpt", "gemini", "grok", "ollama")
# The chat routes own the ENDPOINT vocabulary. This half knows the
# same brain by the name of the tool it actually invokes.
CHAT_ROUTES = {
"claude": "/api/chat",
"codex": "/api/codex/chat", # <- the same brain as "chatgpt"
"gemini": "/api/gemini/chat",
"grok": "/api/grok/chat",
"ollama": "/api/ollama/chat",
}
def ask_broken(brain: str, prompt: str):
"""Every app shipped this. Correct-looking, and dead for one value."""
return post(CHAT_ROUTES[brain], prompt) # KeyError: 'chatgpt'
# --- The boundary, owned in one place -----------------------------
BRAIN_ALIASES = {"chatgpt": "codex", "gpt": "codex"}
def canonical_brain(brain: str) -> str:
"""Settings vocabulary -> routing vocabulary. Applied at the
crossing, never stored back: the picker namespace belongs to the
settings owner, and writing the translated value into settings
would corrupt the very list the user is choosing from."""
key = (brain or "").strip().lower()
return BRAIN_ALIASES.get(key, key)
def ask(brain: str, prompt: str):
return post(CHAT_ROUTES[canonical_brain(brain)], prompt)
# The test that would have caught it is not a unit test of either
# half. It is a test ACROSS the boundary, and it belongs wherever
# the boundary is now owned:
def test_every_picker_value_reaches_a_route():
for value in PICKER_BRAINS:
assert canonical_brain(value) in CHAT_ROUTES, value
Find one boundary in your own system where two components name the same concept differently — status strings, role names, currency codes, event types, anything. Write the crossing assertion: every value producible on side A resolves to something valid on side B. Run it. If it passes, keep it; if it fails, you have just found a defect that no existing test could have reported.
Hint
The best candidates are boundaries where one side is a user-facing vocabulary and the other is an implementation vocabulary, because those two are *supposed* to differ. The danger is not that they differ — it is that the translation between them is performed by each caller from memory.
Progress
Progress is local-only — sign in to sync across devices.