The promise of server-driven UI
Server-driven UI gets sold as a silver bullet: faster iteration, remote configuration, the ability to change UI without shipping a new app version. All of that is true, and it's also why so many SDUI systems end up failing.
Most failures don't come from performance or missing features. They come from a basic misunderstanding of what SDUI actually is. The hard part was never rendering JSON. The hard part is designing UI contracts that stay stable while everything around them keeps changing. Get the contract wrong and SDUI turns into an untyped, untestable, brittle system fast.
Why naive SDUI breaks down
Most SDUI implementations start from a dangerous assumption: if we can describe the UI in JSON, we can render anything. That usually produces something close to HTML-over-the-wire, minus the decades of tooling and standards HTML actually has behind it.
The common failure modes look similar everywhere: treating SDUI as serialized markup, encoding layout, style, and behavior in the same structure, skipping schema validation in the name of flexibility, letting backend responses shape UI structure directly. It feels powerful at first. Given enough time, it becomes impossible to reason about, change safely, or refactor.
Flexibility without constraints isn't power. It's debt with a delay on it.
SDUI is a contract, not a renderer
The mental shift that actually matters: SDUI is a contract between server and client. The renderer is the easy part. The contract is what defines what the server is allowed to ask for, what the client guarantees it can render, and what happens when either side changes. Frame it that way and most of the hard design decisions get easier.
Think in primitives, not screens
The fastest way to break an SDUI system is to model screens or pages. They're too specific, too tied to a particular product concept, too fragile to survive change. Build on primitives instead: text, image, card, list, section, action. These form a shared vocabulary, and meaning comes from composing them, not from writing a new specialized component. Change a primitive and you update it once. Change a screen and you just recombine primitives you already have.
Schema first, UI second
A good SDUI system is schema-driven. The schema should answer, explicitly, what can be rendered, what can't, which fields are optional, which are required, and what the defaults are. Type safety here isn't a nice-to-have, it's what keeps the system alive. Skip schemas and validation moves to runtime, errors surface in production, and refactors get dangerous. The schema is the actual product of an SDUI system; the JSON payload is just its output.
Where SDUI actually shines
SDUI isn't a universal solution. It works well for recommendation and feed-based sections, feature flags and experiments, content-heavy surfaces, and cross-platform UI parity, places where the structure stays stable but the composition changes often. That's where SDUI buys you iteration speed without a client release, while keeping rendering predictable. Trying to drive every pixel of an app through it is usually a mistake.
Designing for evolution
Assume change from the start: new primitives will get added, old ones deprecated, fields will shift, and clients will lag behind servers. A good contract handles all of that without drama. Backward compatibility, sane defaults, and validation aren't afterthoughts here; they're the actual design requirements, not extras bolted on once something breaks.
