Skip to content

Why Contract-First

Legato uses a contract-first model because playback systems fail first at semantic boundaries, not at command wiring.

Runtime integrations move fast: APIs change, platform behaviors differ, and adapter implementations evolve.

Application policy moves slower: product behavior depends on stable meaning for snapshots, events, queue state, and errors.

Contract-first resolves this tension by fixing the shared meaning first, then letting runtime implementations project into that meaning.

@ddgutierrezc/legato-contract defines stable shared semantics (tracks, snapshots, events, errors, capabilities, invariants) without embedding transport behavior.

That separation avoids common failure modes:

  • Semantic drift across layers: different parts of the app interpreting “current track” or “active queue index” differently.
  • Runtime leakage into app logic: domain code coupled to plugin surfaces instead of contract snapshots and event payloads.
  • Brittle refactors: runtime changes forcing unrelated UI/business-policy rewrites.
  • Hard-to-test policy code: needing plugin/runtime setup just to test event and state decisions.
  • Contract package: portable types and constants (Track, PlaybackSnapshot, LEGATO_EVENT_NAMES, LEGATO_INVARIANTS, and related exports).
  • Adapters/bindings: runtime-facing implementations that satisfy contract expectations.
  • Consumers: app logic that reads snapshots, handles typed events, and applies policy decisions.

The result is a shared vocabulary across integration points, with runtime behavior plugged in behind that vocabulary.

Runtime-first contrast (and why Legato does not default to it)

Section titled “Runtime-first contrast (and why Legato does not default to it)”

A runtime-first approach usually starts from plugin methods/events and only later extracts shared semantics.

That can ship quickly for one integration, but it tends to accumulate translation debt:

  • event payload meaning differs between call sites,
  • UI rules are inferred from runtime quirks,
  • and portability to another runtime becomes a rewrite instead of a projection.

Legato’s contract-first posture pays that semantic cost up front so runtime-specific complexity stays local.

  • Stronger interoperability: multiple adapters can project data in the same shape.
  • Safer refactors: consumer code depends on a stable public contract, not runtime internals.
  • Cleaner testing boundaries: you can test contract-level consumers with plain TypeScript objects.
  • It does not provide runtime playback execution by itself; runtime adapters/plugins still own that responsibility.
  • It does not remove platform constraints (for example, capability support remains runtime-projected).
  • It does not guarantee product UX quality automatically; teams still need explicit policy decisions on top of snapshots and events.

Contract-first gives you a stable language. It does not replace runtime engineering or product decision-making.