Version compatibility

← Streaming · Documentation · Next: Extensions →

Old installed clients must not crash when BDUI evolves. Layout compatibility is primarily the server's responsibility: clients advertise their vocabulary, and the server returns a compatible tree.

Spec version

The client publishes an integer:

object BduiSpec {
    const val CURRENT: Int = 11
}

Spec 2 adds the patch action and $ node roots in patch paths ($id.offer-42/state/liked). Spec 3 replaces alert with dialog (presentation: alert, bottomSheet, tooltip; widget content). Spec 4 moves modifier and onClick out of params onto the widget envelope; any widget may declare onClick. Spec 5 adds LazyRow, weight / background / clip / padding modifiers, Card containerColor, Text fontWeight, and container spacing. Spec 6 adds Scaffold, TopAppBar, FloatingActionButton, openScreen, and popScreen. Spec 7 adds the Chart widget (line, area, bar, pie, donut). Spec 8 adds the Calendar and Review widgets and the validate action; TextField gains error, multiline, minLines and keyboard, and RadioGroup gains onChange. Spec 9 adds the Select dropdown widget. Spec 10 adds ref nodes and over-the-air widget definitions (OTA widgets); servers send refs only to clients reporting spec 10 or higher. Spec 11 adds the Stepper container.

Increment it when adding a new kind of:

  • built-in widget or action;
  • state/context path prefix;
  • HTTP apply mode;
  • observer trigger;
  • modifier with new semantics;
  • incompatible meaning for an existing field.

Do not increment it for an additive optional field, a new optional enum value with an old-client-safe default, or an unknown JSON key that old clients can ignore.

Client capability request

Every BDUI document/stream request sends:

X-Bdui-Spec: 11

BDUI-originated HTTP actions and SSE reconnects include it too. If the backend needs app extensions, the host may additionally advertise registered namespaced widget/action names in a compact capability header or request field.

The backend should use the header to select a compatible widget tree and actions before serialization.

Node behavior

Widget nodes may include:

{
  "type": "Carousel",
  "minSpec": 5,
  "children": []
}

A widget is understood when:

  1. minSpec is absent or not above BduiSpec.CURRENT;
  2. type is built in or registered on this BduiConfig.

Otherwise the node and its subtree are skipped. Siblings continue; log only—no placeholder, crash, or toast.

Use child minSpec only when the block is optional. If losing the block makes the flow unusable, serve a different lower-spec layout.

Action behavior

Actions may also include minSpec:

{
  "type": "custom",
  "name": "acme:shareOffer",
  "minSpec": 5,
  "args": { "offerId": "$root.id" }
}

Unknown type, unregistered custom action, or high minSpec is skipped:

  • log;
  • no-op;
  • continue surrounding sync;
  • do not run onError or error observers because compatibility skip is not execution failure.

The backend returns an older action/tree based on X-Bdui-Spec.

Refusing the view

Root minSpec is different:

{
  "id": "root",
  "type": "Column",
  "minSpec": 6,
  "children": []
}

When root minSpec is above the client:

  1. do not commit the document;
  2. stop applying it as a snapshot or a patch that replaces $root;
  3. ask the host to show its update/incompatible UI in this view's slot;
  4. keep the previous valid tree if this was a live update.

This rule applies to initial JSON, SSE snapshot, patch that replaces $root, and restored checkpoints.

Evolution rules

ChangeRequired behavior
New optional param/keyNo bump; old client ignores it
New enum valueOnly no bump when old default is safe; otherwise bump
New widgetBump; server sends old layout or marks optional block minSpec
New action/prefix/apply/triggerBump; server sends old behavior or omits it. patch is spec 2; dialog is spec 3; envelope modifier/onClick is spec 4
HTTP jsMapBump. Old clients would ignore the field and apply the unmapped body — do not send jsMap to a lower X-Bdui-Spec; send a pre-shaped payload instead
Changed semantics for existing fieldPrefer a new field; otherwise bump and require root minSpec
New namespaced app widget/actionNo spec bump; unregistered clients skip
Removing a typeKeep decoder support through deprecation window; server stops sending based on advertised spec

Never reuse an old field/type name for incompatible behavior.

SSE and generation

The client spec controls vocabulary; generation controls stream ordering. They are separate:

  • minSpec: can this client understand the payload?
  • generation: is this update newer than the current store?
  • SSE id: where should reconnect resume?

An incompatible root snapshot is refused. An incompatible child action/node is skipped without disconnecting.

Checkpoints

A checkpoint stores the client spec/schema with which it was decoded. On restore:

  • reject if root minSpec > CURRENT;
  • reject when checkpoint schema cannot migrate;
  • otherwise apply normal node/action compatibility rules;
  • reconnect to obtain current server state.

Server examples

For spec 4:

{
  "id": "root",
  "type": "Column",
  "children": [
    { "type": "Image", "params": { "url": "$root.hero", "contentDescription": "Offer" } }
  ]
}

For spec 5:

{
  "id": "root",
  "type": "Column",
  "children": [
    {
      "type": "Carousel",
      "children": [
        { "type": "Image", "params": { "url": "$root.hero", "contentDescription": "Offer" } }
      ]
    }
  ]
}

The two documents may be produced from one server-side model; clients should not carry substitute layout logic.

← Streaming · Documentation · Next: Extensions →

AppSana — экраны под задачу в приложениях, которые у вас уже есть.