Back to blog
Applications Dec 10, 2025 7 min read

The JSON Descriptor Pattern: How We Made Enterprise UI Generation Deterministic

A contractor list screen in one of our customer deployments is 38 lines of JSON. The hand-written Angular equivalent it replaced was 412 lines of TypeScript, HTML, and SCSS. Same behavior, same data grid, same master-detail relationship to the addresses sub-table. One-tenth the surface area.

That ratio is the reason our AI generation pipeline works in production. It’s also the reason most AI-generated enterprise UI doesn’t.

The problem with generating code directly

When a model generates a React component, it makes hundreds of micro-decisions. Variable names, state management, CSS approach, error handling, data fetching. Run the same prompt twice and you get two working implementations that share almost no structure. For a demo, that’s fine. For a production enterprise system, it’s a non-starter.

Enterprises need reproducibility. They need auditability. They need to point at a screen and prove exactly what it does, who can see what, and how a number was computed. Free-form code generation can’t deliver any of that at the architectural level.

The insight that changed our approach

We spent roughly six months trying different generation strategies before landing on the rule that reorganized everything. Don’t generate the application. Generate the description of the application.

A JSON descriptor specifies what a screen does. Its fields, layout, validation rules, data sources, permissions, and actions. It says nothing about how that screen renders. The framework owns rendering, deterministically, the same way every time.

What a descriptor looks like in practice

A typical list screen descriptor declares the data source endpoint and query parameters, the columns with types and sort behavior, filter and search configuration, the available actions and their permission requirements, and any master-detail relationships to other entities.

That’s it. No JSX, no event handlers, no styling. The framework handles the rendering, the API client handles the network, and the permission layer handles access control. The descriptor is easier to write, easier to review, and dramatically easier to modify.

Why this matters for AI generation

Pointing the model at descriptors instead of code changes four things at once.

Token efficiency. A descriptor is roughly 10x smaller than the equivalent component. Generation is faster and cheaper, and the context window holds more of the surrounding system.

Consistency. The framework renders every descriptor identically. There’s no architectural drift between screens written on Monday and screens written on Tuesday.

Reviewability. A product manager can read a JSON descriptor and understand the screen. They can’t read 400 lines of TypeScript, and they shouldn’t have to.

Diff-ability. When a descriptor changes, the diff shows the actual semantic change. “Added column Priority, type select, options High/Medium/Low.” That’s a meaningful artifact for a human reviewer or a SOX auditor.

The escape hatch

Descriptors aren’t universal. Some screens need custom components, unusual layouts, or behaviors the framework doesn’t anticipate. We handle that with a single rule: any descriptor can reference a custom component written in standard TypeScript.

In our deployments, the framework covers roughly 90% of enterprise screens. The remaining 10% use custom code where it earns its complexity. Even those custom components inherit the framework’s patterns for data access, permissions, and error handling.

The compounding advantage

Every new descriptor makes the system smarter. The AI learns from existing descriptors to draft better ones. The component library absorbs edge cases as they appear. Patterns solved once become available to every future screen.

Over time the system converges toward a state where generating a new enterprise screen takes seconds and the output is production-ready immediately. Not because the model got smarter. Because we made the problem smaller.