Don't write frameworks for dummies.
That sentence stuck with me while reading Domain-Driven Design: Tackling Complexity in the Heart of Software. I didn't fully understand it at first - but after building (and then redesigning) an AI orchestration framework, I do now.
Where We Started
When we began building a framework for orchestrating AI agents, one of the first features we introduced was a unified request interface across multiple LLM providers.
At first, it felt like a great design decision.
One interface.
Multiple providers.
Clean abstraction.
Simple.
But that simplicity turned out to be misleading.
The Problem with "Unified" Abstractions
Over time, cracks started to show:
All models looked the same to developers
By flattening everything into a single interface, we erased the differences between models. Developers stopped thinking about capabilities and limitations - which is exactly what they should be thinking about.
Adding new models became harder, not easier
New models come with new capabilities, parameters, and behaviors. A "unified" interface either ignores those features, or becomes bloated trying to support everything. Neither is good design.
The ubiquitous language was wrong
The API didn't reflect the domain. It reflected our attempt to simplify it. That meant the language of the library didn't express real model capabilities.
We enabled misuse by design
The worst part: the abstraction allowed developers to make mistakes easily - and only discover them at runtime.
For example:
passing reasoning_effort="high" to a model that doesn't support reasoning.
The system didn't prevent it. It allowed it.
That's not just a bad developer experience - it's a failure in design. The abstraction wasn't helping developers. It was hiding the truth.
The Redesign
So we changed direction.
Instead of forcing a unified interface, we started modeling each LLM separately, along with its capabilities and constraints.
Why This Is the Right Move
Advanced users can extract more value
Power users are no longer limited by a lowest-common-denominator API.
Models are not the same - and that matters
Treating them as identical leads to misuse. Embracing differences leads to better outcomes.
Invalid configurations are caught early
Instead of runtime surprises, errors are surfaced immediately.
Better developer experience (DX)
Clear APIs, explicit capabilities, fewer hidden assumptions.
Most importantly: the library expresses domain knowledge correctly
The design now reflects reality - not an oversimplified version of it.
Looking Forward
We're not done yet.
We haven't fully committed the redesign - but we've started moving in this direction, and the changes will roll out in the next versions of Mozaik.
The goal is simple:
make model capabilities explicit
prevent invalid usage early
and let developers actually use the power of each model instead of hiding it
This is still evolving, and we'll likely learn more (and fix more mistakes) along the way.
But one thing is already clear:
We're no longer trying to hide the complexity of LLMs. We're designing for it.
Source:


Top comments (0)