CONCEPT Cited by 1 source
JSON-string parameters for schema stability¶
Definition¶
JSON-string parameters for schema stability is the design choice to carry element-specific content as an opaque JSON string inside a stable, minimal outer schema, rather than encoding every element variant as a distinct typed case in the schema.
In Yelp's CHAOS GraphQL schema, every
component is a ChaosJsonComponent with three visible fields:
identifier, componentType (e.g. chaos.button.v1), and
parameters — a string that the backend populates with
escaped JSON and the client parses into the element-specific
shape.
{
"__typename": "ChaosJsonComponent",
"identifier": "find-local-businesses-button",
"componentType": "chaos.button.v1",
"parameters": "{\"text\": \"Find local businesses\", \"style\": \"primary\", \"onClick\": [\"open-search-url\"]}"
}
Verbatim rationale from the 2025-07-08 post: "Instead of defining individual schemas for each element in the GraphQL layer, we use JSON strings for element content. This approach maintains a stable GraphQL schema and allows for rapid iteration on new elements or versions."
Why it's a real tradeoff, not just laziness¶
Benefits of opaque-string parameters:
- No schema PR for new elements. Adding
chaos.newcomponent.v1requires a backend dataclass and a client renderer — no GraphQL schema change, no supergraph redeployment. - Versioning is in-band via the type string.
chaos.text.v1vschaos.text.v2can coexist in the same response; clients pick the version they support. - Federated schema churn stays low. In an Apollo Federation supergraph, every schema change ripples through the router and tooling. Keeping the CHAOS schema stable means the CHAOS team can iterate on elements without coordinating with the rest of the supergraph.
Costs you're explicitly accepting:
- No GraphQL-level validation of element content. The
GraphQL introspection type is
String, notChaosButtonV1Parameters. Clients get no schema-driven type safety for parameters. - Tooling degrades. GraphQL codegen tools can't generate
typed accessors for
parameters; each platform's client writes a JSON parser and a switch oncomponentType. - Schema drift becomes a correctness problem. The
backend's Python dataclass (
TextV1) and each client's renderer must agree on the parameter shape — via docs, code review, or a separate schema registry — not via GraphQL. - Discoverability suffers. A new engineer looking at the
CHAOS schema sees one
parameters: Stringfield and has to go elsewhere to learn the actual element vocabulary.
When to use this pattern¶
- Rapid-iteration UI systems where the element vocabulary changes faster than the API schema can safely evolve. SDUI frameworks are the archetype.
- Federated GraphQL where every schema change is expensive due to cross-team coordination (patterns/federated-graphql-subgraph-per-domain).
- Client-rendered payloads where the payload is interpreted by a renderer (not joined with other GraphQL fields at query time).
When not to use it¶
- Fields that need to be queried, filtered, or aggregated on the server side — keep those typed.
- APIs consumed by third parties who expect schema-driven type generation.
- Content with security implications (access control, PII) where schema-level field-visibility is load-bearing.
Alternatives¶
- GraphQL unions per element type — fully typed, expensive to evolve, best when element count is small and stable.
- Schema-per-element with @defer / @skip — more modern
GraphQL can use
@oneOfor federated interfaces, but each new element still requires schema work. - Protocol Buffers with
google.protobuf.Any— the equivalent pattern outside GraphQL; same tradeoffs.
Seen in¶
- sources/2025-07-08-yelp-exploring-chaos-building-a-backend-for-server-driven-ui
— Yelp's CHAOS framework uses this pattern for both
components (
ChaosJsonComponent) and actions (ChaosJsonAction); first wiki instance.