Skip to content

SYSTEM Cited by 1 source

Yelp Konbini

Definition

Konbini is Yelp's auto-generated library family that bridges CHAOS (Yelp's server-driven-UI framework) to Cookbook (Yelp's cross-platform design system). From a single JSON interface definition per Cookbook component, Konbini generates four platform-specific libraries that all stay perfectly in sync because they are generated from the same source file:

  • componentinterfaces — Kotlin package for Android clients (Moshi-based JSON deserialisation).
  • YLInterfaces — Swift package for iOS clients.
  • component_interfaces — Python package for backend services (serialisation to JSON).
  • component-interfaces — TypeScript/React package for web clients.

Konbini is built by Yelp's Design Systems team. Canonical disclosure: Yelp's 2026-04-22 post "How Yelp Keeps Server-Driven UI Consistent Across Four Platforms".

Why Konbini exists

Before Konbini, different teams implementing CHAOS backends invented their own backend representations for the same UI component. Yelp's post shows two pre-Konbini backends both rendering a "Click me to go to Yelp" button — one called it SDUIButton(label, button_type, action=OpenURLAction(url), size="small"), the other ServerButton(text, style, click_handler={"type": "open_url", "url": ...}, display_size="sm"). Different property names, different action shapes, different size vocabularies. Yelp: "Both solutions were trying to render the same button component, but their backend representations were completely different—different property names, different ways to handle actions, different size values. This created a maintenance burden and made it difficult to ensure a unified user experience."

CHAOS unified SDUI architecturally — one backend shape per view, one GraphQL subgraph — but still allowed per-team component vocabularies. Konbini unifies the component interface contract: every CHAOS backend at Yelp now instantiates the same generated CookbookButton class (or any other Cookbook-defined component), whose output is bit-compatible with the clients' same generated deserialisers on Kotlin, Swift, and TypeScript.

How it works

Single JSON source

Every Cookbook component has one JSON interface definition in the component_interfaces repository:

{
  "name": "cookbook.Button",
  "version": "0.8",
  "description": "A customizable Cookbook button.",
  "owners": ["Design Systems <design-systems@yelp.com>"],
  "parameters": {
    "text":             { "type": "String", "description": "..." },
    "style":            { "type": "cookbook.ButtonStyle", "description": "..." },
    "on_click":         { "type": "Nullable<Action>", "default": null,
                          "description": "..." },
    "size":             { "type": "cookbook.ButtonSize", "default": "standard",
                          "description": "..." },
    "background_color": { "type": "Color", "description": "..." }
  }
}

Jenkins-triggered codegen

Verbatim: "Whenever a new commit is pushed to the component_interfaces repository, Jenkins pipelines automatically trigger the code generation process and publish new versions of the libraries." One push, four published libraries.

Same-name guarantee across platforms

The generated Python backend serialiser (CookbookButtonV0) and the generated Kotlin deserialiser (CookbookButtonInterfaceParams) both carry the same parameter names (text, style, on_click, size, background_color) because both are derived from the same JSON definition. Yelp: "Because both the Python and Kotlin code are generated from the same JSON source, the parameter names are guaranteed to stay in sync." See patterns/single-json-spec-to-multi-platform-codegen.

Separation of concerns: Konbini + hand-written render

Konbini stops at the deserialised interface model. The binding from the model to the platform-native Cookbook widget is hand-written per platform. In Kotlin/Compose, Yelp writes an extension method:

@Composable
fun CookbookButtonInterface.Render(
    renderer: KonbiniComposeRenderer,
    onError: (RenderError) -> Unit,
    modifier: Modifier = Modifier,
) { /* ... dispatches on size → ButtonSmall/Standard/Large */ }

This is the only layer that couples the Konbini interface model to platform-specific widget composition; it's small and usually invariant across component version bumps.

Spec files and backward compatibility

Each Konbini-generated client library bundles a spec file listing the versions of every interface + enumeration that platform supports. Example:

{
  "version": "23.0",
  "interfaces":    { "cookbook.Button": "1.0" },
  "enumerations":  { "cookbook.ButtonStyle": "0.1" }
}

Every CHAOS request sends a Konbini context (spec_name@spec_version, e.g. android@23.0) to the backend. The backend looks up the spec and picks the highest component version compatible with that spec to serialise.

See concepts/client-spec-version for the spec-file concept and patterns/spec-version-negotiation-for-backward-compat for the per-request version-negotiation pattern.

Breaking changes and migrate()

When a component introduces a breaking change — e.g. Button's text changes from String to a custom FormattedText type — the component major version bumps (0.8 → 1.0). Konbini auto-generates a stub migrate() method on the new class; developers must implement it to produce the previous major-version instance from the new one:

def migrate(model: CookbookButtonV1) -> CookbookButtonV0:
    return CookbookButtonV0(
        text=model.text.toString(),
        style=model.style,
        on_click=model.on_click,
        size=model.size,
        background_color=model.background_color,
    )

If the backend wants to send CookbookButtonV1 but the client only supports V0 per its spec, migrate() is invoked to downgrade. Default behaviour: throw. "We highly recommend replacing this behavior with your own custom logic that uses the data from the provided ... to return an object of type ..." — backward-compat opt-out is an explicit developer action, not accidental. See concepts/component-version-migrate-function and patterns/migrate-function-for-component-downgrade.

Design tokens

Konbini handles another category alongside primitive types: design tokens — named stylistic values (colours, icons, gradients, shadows). Tokens are owned in a separate designer-curated repo, published as JSON, and serialised on the wire as {name, raw_value}:

{
  "color": {
    "name": "ref-color-black-100",
    "raw_value": {"a": 1, "b": 0, "g": 0, "r": 0}
  }
}

The name is stable across platforms; the raw_value is the platform-resolvable default. See concepts/design-token-as-named-reference.

Position on the wiki

Konbini is the wiki's canonical instance of:

Konbini sits below CHAOS: CHAOS builders (FeatureProviders) instantiate Konbini-generated Python classes (CookbookButton(text=..., style=..., ...)) and the CHAOS subgraph then serialises them via the JSON-string parameters mechanism in its GraphQL schema. Konbini-generated client libraries deserialise the JSON content; hand-written render extensions bind the deserialised model to Cookbook widgets.

Known tradeoffs

  • Render extension is hand-written — codegen does not reach all the way to the widget tree. Render-extension regressions are possible even while serialisation stays bit-compatible.
  • migrate() correctness is developer responsibility — the generated default throws. A wrong migration silently ships the wrong backport.
  • Cross-major migrate chains not discussed — the post shows V1 → V0. If V2.0 ships while some clients still pin V0, whether migrate composes is not stated.
  • No disclosed operational numbers — component count, generation time, library publish frequency, backward-compat hit rate. Architectural walkthrough only.

Seen in

Last updated · 550 distilled / 1,221 read