Four cascading bugs, one missing registry
A source-kind adapter registry looked like a code-quality cleanup: replace a scattered if/else with a dispatch table. Closing it closed four bugs that had accumulated across three versions, each one a downstream consequence of branching logic that lived in too many places at once.
When I introduced a source-kind adapter registry to the app, it looked like a code quality call: replace a scattered if/else pattern with a dispatch table. The outcome was different from what I expected. Closing the registry closed four bugs that had accumulated across v0.0.3, v0.0.4, and v0.0.5 in cascade, each one a downstream consequence of a branching pattern that lived in too many places at once.
The pattern that kept growing
The original code handled each source kind (RSS, scraping) with a conditional wherever the kind needed to be known. Two branches, clear enough. By v0.0.5 there were four omitted paths in cascade: a branch added to one handler, skipped in another, mismatched in a third. None wrong in isolation; together they produced a broken RSS selector, a scraping banner that read as broken UI, a SQL crash in admin/usage, and a rate-limit surface with no feedback to the user.
A registry fails early, an else-if fails late
The registry declares all source kinds in one place and routes behavior through adapters keyed by kind. When a source kind is missing, the dispatch point raises the error immediately. When a branch is missing from an if/else chain, the error surfaces in whichever handler didn’t get the update, often far from the root cause and later in the flow. The four bugs came from the same gap: one source kind not propagated consistently. One registry entry to add. Four handlers to audit.
The bigger point
Centralizing dispatch into a registry doesn’t reduce the number of cases to handle. It reduces the number of places each case has to be remembered. That distinction matters as source kinds grow.