Module builders and handles
#7: Plugging the leakiest of abstractions.
Something had been bothering me since the very first PR, in which a builder pattern for constructing modules was first introduced. It knew about every parameter in ever module and relied on several match statements (akin to switch statements in other programming language) to map fields in the JSON document used to define a composition and its runtime counterpart.
For me, a switch statement within a builder or factory patter is a red flag. This is because the worst case of a switch statement is linear (O(n)) complexity. When the scope of the builder is small and is certain to remain that way, this if fine. In practice, O(n) feels like O(1) when n is itself a small constant. However, when the bounds are not known — as in this case, where the builder may eventually need to handle a number modules not known even at compile time — it becomes a bottleneck. It reminds me of a lesson from way back, during my time working on Zynga Poker. That team had incredible discipline and maintained a clean codebase, but as always, one or two poor choices from the distant past — that were likely good choices at the time — lingered.
The primary API, itself a legacy system, was in PHP. While the details are now a bit sketchy, a builder pattern parsed incoming requests using a massive switch statement. It had reached a length of well into the thousands, each case also containing additional business logic. This created two painful bottlenecks:
Measurable latency.
A measurable negative impact on development velocity.
The fix is conceptually simple: use a dictionary to map metadata to types and get constant time complexity and let individual components build themselves. At a certain scale, though, the tech debt is great enough that fixing it seems too time-consuming to prioritize when business needs need to be met.
So when in doubt, I avoid switch statements. When I seem them, I always ask myself it is was the right choice. It was time to fix the this in Fugue before it got out of hand.
I would like `Module` implementations to be as self contained as possible and for the system to require as little knowledge of individual Modules as possible. For example, I would like for each module to own its own build step. Please analyze the codebase and make suggestions with this in mind.
After a pretty significant conversation, we settled on a plan where the library defines a registry and each module builds and validates itself based on the JSON document metadata. It was more difficult than I expected. A pattern I have noticed is that an agent’s eagerness to provide instant gratification often comes at the expense of long-term extensibility. Despite having the ability to execute complex tasks in the short amount of time, it is surprising how often the phrase “for now” appears in conversations. I am learning to only accept those solutions if “now” means a few more coding sessions.
#7: Module builders and handles
2026.1.29



